diff --git a/src/main/java/com/avaje/ebean/AdminAutofetch.java b/src/main/java/com/avaje/ebean/AdminAutofetch.java index c9c7046ee..15e19f9c1 100644 --- a/src/main/java/com/avaje/ebean/AdminAutofetch.java +++ b/src/main/java/com/avaje/ebean/AdminAutofetch.java @@ -8,28 +8,28 @@ public interface AdminAutofetch { /** * Return true if profiling is enabled. */ - public boolean isProfiling(); + boolean isProfiling(); /** * Set to true to enable profiling. */ - public void setProfiling(boolean enable); + void setProfiling(boolean enable); /** * Return true if autoFetch automatic query tuning is enabled. */ - public boolean isQueryTuning(); + boolean isQueryTuning(); /** * Set to true to enable autoFetch automatic query tuning. */ - public void setQueryTuning(boolean enable); + void setQueryTuning(boolean enable); /** * Returns the rate which profiling is collected. This is an int between 0 and * 100. */ - public double getProfilingRate(); + double getProfilingRate(); /** * Set the rate at which profiling is collected after the base. @@ -37,13 +37,13 @@ public interface AdminAutofetch { * @param rate * a int between 0 and 100. */ - public void setProfilingRate(double rate); + void setProfilingRate(double rate); /** * Return the number of queries profiled after which profiling is collected at * a percentage rate. */ - public int getProfilingBase(); + int getProfilingBase(); /** * Set a base number of queries to profile per query point. @@ -52,7 +52,7 @@ public interface AdminAutofetch { * the Profiling Percentage rate. *

*/ - public void setProfilingBase(int profilingBase); + void setProfilingBase(int profilingBase); /** * Return the minimum number of queries profiled before autoFetch will start @@ -62,7 +62,7 @@ public interface AdminAutofetch { * profiling information is collected. *

*/ - public int getProfilingMin(); + int getProfilingMin(); /** * Set the minimum number of queries profiled per query point before autoFetch @@ -72,13 +72,13 @@ public interface AdminAutofetch { * autoFetch starts tuning the query. *

*/ - public void setProfilingMin(int autoFetchMinThreshold); + void setProfilingMin(int autoFetchMinThreshold); /** * Fire a garbage collection (hint to the JVM). Assuming garbage collection * fires this will gather the usage profiling information. */ - public String collectUsageViaGC(); + String collectUsageViaGC(); /** * This will take the current profiling information and update the "tuned @@ -89,7 +89,7 @@ public interface AdminAutofetch { * * @return a summary of the updates that occurred */ - public String updateTunedQueryInfo(); + String updateTunedQueryInfo(); /** * Clear all the tuned query info. @@ -99,7 +99,7 @@ public interface AdminAutofetch { * * @return the amount of tuned query information cleared. */ - public int clearTunedQueryInfo(); + int clearTunedQueryInfo(); /** * Clear all the profiling information. @@ -112,26 +112,26 @@ public interface AdminAutofetch { * * @return the amount of profiled information cleared. */ - public int clearProfilingInfo(); + int clearProfilingInfo(); /** * Clear the query execution statistics. */ - public void clearQueryStatistics(); + void clearQueryStatistics(); /** * Return the number of queries tuned by AutoFetch. */ - public int getTotalTunedQueryCount(); + int getTotalTunedQueryCount(); /** * Return the size of the TuneQuery map. */ - public int getTotalTunedQuerySize(); + int getTotalTunedQuerySize(); /** * Return the size of the profile map. */ - public int getTotalProfileSize(); + int getTotalProfileSize(); } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/BackgroundExecutor.java b/src/main/java/com/avaje/ebean/BackgroundExecutor.java index f3b86b79b..12edb0a51 100644 --- a/src/main/java/com/avaje/ebean/BackgroundExecutor.java +++ b/src/main/java/com/avaje/ebean/BackgroundExecutor.java @@ -1,40 +1,40 @@ -package com.avaje.ebean; - -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -/** - * Background thread pool service for executing of tasks asynchronously. - *

- * This service is used internally by Ebean for executing background tasks such - * as the {@link Query#findFutureList()} and also for executing background tasks - * periodically. - *

- *

- * This service has been made available so you can use it for your application - * code if you want. It can be useful for some server caching implementations - * (background population and trimming of the cache etc). - *

- * - * @author rbygrave - */ -public interface BackgroundExecutor { - - /** - * Execute a task in the background. - */ - public void execute(Runnable r); - - /** - * Execute a task periodically with a fixed delay between each execution. - *

- * For example, execute a runnable every minute. - *

- *

- * The delay is the time between executions no matter how long the task took. - * That is, this method has the same behaviour characteristics as - * {@link ScheduledExecutorService#scheduleWithFixedDelay(Runnable, long, long, TimeUnit)} - *

- */ - public void executePeriodically(Runnable r, long delay, TimeUnit unit); -} +package com.avaje.ebean; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * Background thread pool service for executing of tasks asynchronously. + *

+ * This service is used internally by Ebean for executing background tasks such + * as the {@link Query#findFutureList()} and also for executing background tasks + * periodically. + *

+ *

+ * This service has been made available so you can use it for your application + * code if you want. It can be useful for some server caching implementations + * (background population and trimming of the cache etc). + *

+ * + * @author rbygrave + */ +public interface BackgroundExecutor { + + /** + * Execute a task in the background. + */ + void execute(Runnable r); + + /** + * Execute a task periodically with a fixed delay between each execution. + *

+ * For example, execute a runnable every minute. + *

+ *

+ * The delay is the time between executions no matter how long the task took. + * That is, this method has the same behaviour characteristics as + * {@link ScheduledExecutorService#scheduleWithFixedDelay(Runnable, long, long, TimeUnit)} + *

+ */ + void executePeriodically(Runnable r, long delay, TimeUnit unit); +} diff --git a/src/main/java/com/avaje/ebean/BeanState.java b/src/main/java/com/avaje/ebean/BeanState.java index ae290c37a..78017fb03 100644 --- a/src/main/java/com/avaje/ebean/BeanState.java +++ b/src/main/java/com/avaje/ebean/BeanState.java @@ -1,86 +1,86 @@ -package com.avaje.ebean; - -import java.beans.PropertyChangeListener; -import java.util.Map; -import java.util.Set; - -/** - * Provides access to the internal state of an entity bean. - */ -public interface BeanState { - - /** - * Return true if this is a lazy loading reference bean. - *

- * If so the this bean only holds the Id property and will invoke lazy loading - * if any other property is get or set. - *

- */ - public boolean isReference(); - - /** - * Return true if the bean is new (and not yet saved). - */ - public boolean isNew(); - - /** - * Return true if the bean is new or dirty (and probably needs to be saved). - */ - public boolean isNewOrDirty(); - - /** - * Return true if the bean has been changed but not yet saved. - */ - public boolean isDirty(); - - /** - * For partially populated beans returns the properties that are loaded on the - * bean. - *

- * Accessing another property will cause lazy loading to occur. - *

- */ - public Set getLoadedProps(); - - /** - * Return the set of changed properties. - */ - public Set getChangedProps(); - - /** - * Return a map of the updated properties and their new and old values. - */ - public Map getDirtyValues(); - - /** - * Return true if the bean is readOnly. - *

- * If a setter is called on a readOnly bean it will throw an exception. - *

- */ - public boolean isReadOnly(); - - /** - * Set the readOnly status for the bean. - */ - public void setReadOnly(boolean readOnly); - - /** - * Add a propertyChangeListener. - */ - public void addPropertyChangeListener(PropertyChangeListener listener); - - /** - * Remove a propertyChangeListener. - */ - public void removePropertyChangeListener(PropertyChangeListener listener); - - /** - * Advanced - Used to programmatically build a partially or fully loaded - * entity bean. First create an entity bean via - * {@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. - */ - public void setLoaded(); +package com.avaje.ebean; + +import java.beans.PropertyChangeListener; +import java.util.Map; +import java.util.Set; + +/** + * Provides access to the internal state of an entity bean. + */ +public interface BeanState { + + /** + * Return true if this is a lazy loading reference bean. + *

+ * If so the this bean only holds the Id property and will invoke lazy loading + * if any other property is get or set. + *

+ */ + boolean isReference(); + + /** + * Return true if the bean is new (and not yet saved). + */ + boolean isNew(); + + /** + * Return true if the bean is new or dirty (and probably needs to be saved). + */ + boolean isNewOrDirty(); + + /** + * Return true if the bean has been changed but not yet saved. + */ + boolean isDirty(); + + /** + * For partially populated beans returns the properties that are loaded on the + * bean. + *

+ * Accessing another property will cause lazy loading to occur. + *

+ */ + Set getLoadedProps(); + + /** + * Return the set of changed properties. + */ + Set getChangedProps(); + + /** + * Return a map of the updated properties and their new and old values. + */ + Map getDirtyValues(); + + /** + * Return true if the bean is readOnly. + *

+ * If a setter is called on a readOnly bean it will throw an exception. + *

+ */ + boolean isReadOnly(); + + /** + * Set the readOnly status for the bean. + */ + void setReadOnly(boolean readOnly); + + /** + * Add a propertyChangeListener. + */ + void addPropertyChangeListener(PropertyChangeListener listener); + + /** + * Remove a propertyChangeListener. + */ + void removePropertyChangeListener(PropertyChangeListener listener); + + /** + * Advanced - Used to programmatically build a partially or fully loaded + * entity bean. First create an entity bean via + * {@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. + */ + void setLoaded(); } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/CallableSql.java b/src/main/java/com/avaje/ebean/CallableSql.java index d6e13f635..f29f22ee0 100644 --- a/src/main/java/com/avaje/ebean/CallableSql.java +++ b/src/main/java/com/avaje/ebean/CallableSql.java @@ -85,17 +85,17 @@ public interface CallableSql { /** * Set the label that is put in the transaction log. */ - public CallableSql setLabel(String label); + CallableSql setLabel(String label); /** * Return the statement execution timeout. */ - public int getTimeout(); + int getTimeout(); /** * Return the callable sql. */ - public String getSql(); + String getSql(); /** * Set the statement execution timeout. Zero implies unlimited time. @@ -103,12 +103,12 @@ public interface CallableSql { * This is set to the underlying CallableStatement. *

*/ - public CallableSql setTimeout(int secs); + CallableSql setTimeout(int secs); /** * Set the callable sql. */ - public CallableSql setSql(String sql); + CallableSql setSql(String sql); /** * Bind a parameter that is bound as a IN parameter. @@ -125,7 +125,7 @@ public interface CallableSql { * @param value * the value of the parameter. */ - public CallableSql bind(int position, Object value); + CallableSql bind(int position, Object value); /** * Bind a positioned parameter (same as bind method). @@ -135,7 +135,7 @@ public interface CallableSql { * @param value * the value of the parameter. */ - public CallableSql setParameter(int position, Object value); + CallableSql setParameter(int position, Object value); /** * Register an OUT parameter. @@ -153,7 +153,7 @@ public interface CallableSql { * @param type * the jdbc type of the OUT parameter that will be read. */ - public CallableSql registerOut(int position, int type); + CallableSql registerOut(int position, int type); /** * Return an OUT parameter value. @@ -165,7 +165,7 @@ public interface CallableSql { * in batch mode you effectively can't use this method. *

*/ - public Object getObject(int position); + Object getObject(int position); /** * @@ -173,7 +173,7 @@ public interface CallableSql { * stored procedure calls. This would be the case when ResultSets are returned * etc. */ - public boolean executeOverride(CallableStatement cstmt) throws SQLException; + boolean executeOverride(CallableStatement cstmt) throws SQLException; /** * Add table modification information to the TransactionEvent. @@ -188,7 +188,6 @@ public interface CallableSql { * delete. *

*/ - public CallableSql addModification(String tableName, boolean inserts, boolean updates, - boolean deletes); + CallableSql addModification(String tableName, boolean inserts, boolean updates, boolean deletes); } \ 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 006753624..cc6a42b31 100644 --- a/src/main/java/com/avaje/ebean/Ebean.java +++ b/src/main/java/com/avaje/ebean/Ebean.java @@ -1,1501 +1,1501 @@ -package com.avaje.ebean; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import javax.persistence.OptimisticLockException; -import javax.persistence.PersistenceException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.avaje.ebean.annotation.CacheStrategy; -import com.avaje.ebean.cache.ServerCacheManager; -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebean.text.csv.CsvReader; -import com.avaje.ebean.text.json.JsonContext; - -/** - * This Ebean object is effectively a singleton that holds a map of registered - * {@link EbeanServer}s. It additionally provides a convenient way to use the - * 'default/primary' EbeanServer. - *

- * If you are using a Dependency Injection framework such as - * Spring or Guice you will probably - * NOT use this Ebean singleton object. Instead you will - * configure and construct EbeanServer instances using {@link ServerConfig} and - * {@link EbeanServerFactory} and inject those EbeanServer instances into your - * data access objects. - *

- *

- * In documentation "Ebean singleton" refers to this object. - *

- *
    - *
  • There is one EbeanServer per Database (javax.sql.DataSource).
  • - *
  • EbeanServers can be 'registered' with the Ebean singleton (put into its - * map). Registered EbeanServer's can later be retrieved via - * {@link #getServer(String)}.
  • - *
  • One EbeanServer can be referred to as the 'default' EbeanServer. For - * convenience, the Ebean singleton (this object) provides methods such as - * {@link #find(Class)} that proxy through to the 'default' EbeanServer. This - * can be useful for applications that use a single database.
  • - *
- * - *

- * For developer convenience Ebean has static methods that proxy through to the - * methods on the 'default' EbeanServer. These methods are provided for - * developers who are mostly using a single database. Many developers will be - * able to use the methods on Ebean rather than get a EbeanServer. - *

- *

- * EbeanServers can be created and used without ever needing or using the Ebean - * singleton. Refer to {@link ServerConfig#setRegister(boolean)}. - *

- *

- * You can either programmatically create/register EbeanServers via - * {@link EbeanServerFactory} or they can automatically be created and - * registered when you first use the Ebean singleton. When EbeanServers are - * created automatically they are configured using information in the - * ebean.properties file. - *

- * - *
{@code
- *
- *   // fetch shipped orders (and also their customer)
- *   List list = Ebean.find(Order.class)
- * 	  .fetch("customer")
- * 	  .where()
- * 	  .eq("status.code", Order.Status.SHIPPED)
- * 	  .findList();
- *
- *   // read/use the order list ...
- *   for (Order order : list) {
- * 	   Customer customer = order.getCustomer();
- * 	   ...
- *   }
- *
- * }
- * - *
{@code
- *
- *   // fetch order 10, modify and save
- *   Order order = Ebean.find(Order.class, 10);
- * 
- *   OrderStatus shipped = Ebean.getReference(OrderStatus.class,"SHIPPED");
- *   order.setStatus(shipped);
- *   order.setShippedDate(shippedDate);
- *   ...
- * 
- *   // implicitly creates a transaction and commits
- *   Ebean.save(order);
- *
- * }
- * - *

- * When you have multiple databases and need access to a specific one the - * {@link #getServer(String)} method provides access to the EbeanServer for that - * specific database. - *

- * - *
 {@code
- *
- *   // Get access to the Human Resources EbeanServer/Database
- *   EbeanServer hrDb = Ebean.getServer("hr");
- * 
- * 
- *   // fetch contact 3 from the HR database
- *   Contact contact = hrDb.find(Contact.class, 3);
- * 
- *   contact.setName("I'm going to change");
- *   ...
- * 
- *   // save the contact back to the HR database
- *   hrDb.save(contact);
- *
- * }
- */ -public final class Ebean { - private static final Logger logger = LoggerFactory.getLogger(Ebean.class); - - /** - * Manages creation and cache of EbeanServers. - */ - private static final Ebean.ServerManager serverMgr = new Ebean.ServerManager(); - - /** - * Helper class for managing fast and safe access and creation of - * EbeanServers. - */ - private static final class ServerManager { - - /** - * Cache for fast concurrent read access. - */ - private final ConcurrentHashMap concMap = new ConcurrentHashMap(); - - /** - * Cache for synchronized read, creation and put. Protected by the monitor - * object. - */ - private final HashMap syncMap = new HashMap(); - - private final Object monitor = new Object(); - - /** - * The 'default/primary' EbeanServer. - */ - private EbeanServer primaryServer; - - private ServerManager() { - - try { - // skipDefaultServer is set by EbeanServerFactory - // ... when it is creating the primaryServer - if (PrimaryServer.isSkip()) { - // primary server being created by EbeanServerFactory - // ... so we should not try and create it here - logger.debug("PrimaryServer.isSkip()"); - - } else { - // look to see if there is a default server defined - String primaryName = PrimaryServer.getPrimaryServerName(); - logger.debug("primaryName:" + primaryName); - if (primaryName != null && primaryName.trim().length() > 0) { - primaryServer = getWithCreate(primaryName.trim()); - } - } - } catch (RuntimeException e) { - logger.error("Error trying to create the default EbeanServer", e); - throw e; - } - } - - private EbeanServer getPrimaryServer() { - if (primaryServer == null) { - String msg = "The default EbeanServer has not been defined?"; - msg += " This is normally set via the ebean.datasource.default property."; - msg += " Otherwise it should be registered programatically via registerServer()"; - throw new PersistenceException(msg); - } - return primaryServer; - } - - private EbeanServer get(String name) { - if (name == null || name.length() == 0) { - return primaryServer; - } - // non-synchronized read - EbeanServer server = concMap.get(name); - if (server != null) { - return server; - } - // synchronized read, create and put - return getWithCreate(name); - } - - /** - * Synchronized read, create and put of EbeanServers. - */ - private EbeanServer getWithCreate(String name) { - - synchronized (monitor) { - - EbeanServer server = syncMap.get(name); - if (server == null) { - // register when creating server this way - server = EbeanServerFactory.create(name); - register(server, false); - } - return server; - } - } - - /** - * Register a server so we can get it by its name. - */ - private void register(EbeanServer server, boolean isPrimaryServer) { - registerWithName(server.getName(), server, isPrimaryServer); - } - - private void registerWithName(String name, EbeanServer server, boolean isPrimaryServer) { - synchronized (monitor) { - concMap.put(name, server); - syncMap.put(name, server); - if (isPrimaryServer) { - primaryServer = server; - } - } - } - - } - - private Ebean() { - } - - /** - * Get the EbeanServer for a given DataSource. If name is null this will - * return the 'default' EbeanServer. - *

- * This is provided to access EbeanServer for databases other than the - * 'default' database. EbeanServer also provides more control over - * transactions and the ability to use transactions created externally to - * Ebean. - *

- * - *
{@code
-   * // use the "hr" database
-   * EbeanServer hrDatabase = Ebean.getServer("hr");
-   * 
-   * Person person = hrDatabase.find(Person.class, 10);
-   * }
- * - * @param name - * the name of the server, use null for the 'default server' - */ - public static EbeanServer getServer(String name) { - return serverMgr.get(name); - } - - /** - * Return the ExpressionFactory from the default server. - *

- * The ExpressionFactory is used internally by the query and ExpressionList to - * build the WHERE and HAVING clauses. Alternatively you can use the - * ExpressionFactory directly to create expressions to add to the query where - * clause. - *

- *

- * Alternatively you can use the {@link Expr} as a shortcut to the - * ExpressionFactory of the 'Default' EbeanServer. - *

- *

- * You generally need to the an ExpressionFactory (or {@link Expr}) to build - * an expression that uses OR like Expression e = Expr.or(..., ...); - *

- */ - public static ExpressionFactory getExpressionFactory() { - return serverMgr.getPrimaryServer().getExpressionFactory(); - } - - /** - * Register the server with this Ebean singleton. Specify if the registered - * server is the primary/default server. - */ - public static void register(EbeanServer server, boolean isPrimaryServer) { - serverMgr.register(server, isPrimaryServer); - } - - /** - * Backdoor for registering a mock implementation of EbeanServer as the default server. - */ - protected static EbeanServer mock(String name, EbeanServer server, boolean isPrimaryServer) { - EbeanServer originalPrimaryServer = serverMgr.primaryServer; - serverMgr.registerWithName(name, server, isPrimaryServer); - return originalPrimaryServer; - } - - /** - * Return the next identity value for a given bean type. - *

- * This will only work when a IdGenerator is on this bean type such as a DB - * sequence or UUID. - *

- *

- * For DB's supporting getGeneratedKeys and sequences such as Oracle10 you do - * not need to use this method generally. It is made available for more - * complex cases where it is useful to get an ID prior to some processing. - *

- */ - public static Object nextId(Class beanType) { - return serverMgr.getPrimaryServer().nextId(beanType); - } - - /** - * Start a new explicit transaction. - *

- * 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)
-   *   Ebean.beginTransaction();
-   *   try {
-   * 	   Order order = Ebean.find(Order.class,10); ...
-   *
-   * 	   Ebean.save(order);
-   * 
-   * 	   Ebean.commitTransaction();
-   * 
-   *   } finally {
-   * 	   // rollback if we didn't commit
-   * 	   // i.e. an exception occurred before commitTransaction().
-   * 	   Ebean.endTransaction();
-   *   }
-   *
-   * }
- * - *

- * If you want to externalise the transaction management then you should be - * able to do this via EbeanServer. Specifically with EbeanServer you can pass - * the transaction to the various find() and save() execute() methods. This - * gives you the ability to create the transactions yourself externally from - * Ebean and pass those transactions through to the various methods available - * on EbeanServer. - *

- */ - public static Transaction beginTransaction() { - return serverMgr.getPrimaryServer().beginTransaction(); - } - - /** - * Start a transaction additionally specifying the isolation level. - * - * @param isolation - * the Transaction isolation level - * - */ - public static Transaction beginTransaction(TxIsolation isolation) { - return serverMgr.getPrimaryServer().beginTransaction(isolation); - } - - /** - * Start a transaction typically specifying REQUIRES_NEW or REQUIRED semantics. - * - *

- * Note that this provides an try finally alternative to using {@link #execute(TxScope, TxCallable)} or - * {@link #execute(TxScope, TxRunnable)}. - *

- * - *

REQUIRES_NEW example:

- *
{@code
-   * // Start a new transaction. If there is a current transaction
-   * // suspend it until this transaction ends
-   * Transaction txn = Ebean.beginTransaction(TxScope.requiresNew());
-   * try {
-   *
-   *   ...
-   *
-   *   // commit the transaction
-   *   txn.commit();
-   *
-   * } finally {
-   *   // end this transaction which:
-   *   //  A) will rollback transaction if it has not been committed already
-   *   //  B) will restore a previously suspended transaction
-   *   txn.end();
-   * }
-   *
-   * }
- * - *

REQUIRED example:

- *
{@code
-   *
-   * // start a new transaction if there is not a current transaction
-   * Transaction txn = Ebean.beginTransaction(TxScope.required());
-   * try {
-   *
-   *   ...
-   *
-   *   // commit the transaction if it was created or
-   *   // do nothing if there was already a current transaction
-   *   txn.commit();
-   *
-   * } finally {
-   *   // end this transaction which will rollback the transaction
-   *   // if it was created for this try finally scope and has not
-   *   // already been committed
-   *   txn.end();
-   * }
-   *
-   * }
- */ - public static Transaction beginTransaction(TxScope scope){ - return serverMgr.getPrimaryServer().beginTransaction(scope); - } - - /** - * Returns the current transaction or null if there is no current transaction - * in scope. - */ - public static Transaction currentTransaction() { - return serverMgr.getPrimaryServer().currentTransaction(); - } - - /** - * Register a TransactionCallback on the currently active transaction. - *

- * If there is no currently active transaction then a PersistenceException is thrown. - * - * @param transactionCallback the transaction callback to be registered with the current transaction - * - * @throws PersistenceException if there is no currently active transaction - */ - public static void register(TransactionCallback transactionCallback) throws PersistenceException { - serverMgr.getPrimaryServer().register(transactionCallback); - } - - /** - * Commit the current transaction. - */ - public static void commitTransaction() { - serverMgr.getPrimaryServer().commitTransaction(); - } - - /** - * Rollback the current transaction. - */ - public static void rollbackTransaction() { - serverMgr.getPrimaryServer().rollbackTransaction(); - } - - /** - * If the current transaction has already been committed do nothing otherwise - * rollback the transaction. - *

- * Useful to put in a finally block to ensure the transaction is ended, rather - * than a rollbackTransaction() in each catch block. - *

- *

- * Code example: - *

- * - *
{@code
-   *   Ebean.beginTransaction();
-   *   try {
-   *     // do some fetching and or persisting
-   *
-   *     // commit at the end
-   *     Ebean.commitTransaction();
-   * 
-   *   } finally {
-   *     // if commit didn't occur then rollback the transaction
-   *     Ebean.endTransaction();
-   *   }
-   * }
- */ - public static void endTransaction() { - serverMgr.getPrimaryServer().endTransaction(); - } - - /** - * Return a map of the differences between two objects of the same type. - *

- * When null is passed in for b, then the 'OldValues' of a is used for the - * difference comparison. - *

- */ - public static Map diff(Object a, Object b) { - return serverMgr.getPrimaryServer().diff(a, b); - } - - /** - * 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 static void save(Object bean) throws OptimisticLockException { - serverMgr.getPrimaryServer().save(bean); - } - - /** - * Insert the bean. This is useful when you set the Id property on a bean and - * want to explicitly insert it. - */ - public static void insert(Object bean) { - serverMgr.getPrimaryServer().insert(bean); - } - - /** - * Insert a collection of beans. - */ - public static void insert(Collection beans) { - serverMgr.getPrimaryServer().insert(beans); - } - - /** - * Marks the entity bean as dirty. - *

- * This is used so that when a bean that is otherwise unmodified is updated with the version - * property updated. - *

- * An unmodified bean that is saved or updated is normally skipped and this marks the bean as - * dirty so that it is not skipped. - * - *

{@code
-   * 
-   *   Customer customer = Ebean.find(Customer, id);
-   * 
-   *   // mark the bean as dirty so that a save() or update() will
-   *   // increment the version property
-   *   Ebean.markAsDirty(customer);
-   *   Ebean.save(customer);
-   * 
-   * }
- */ - public static void markAsDirty(Object bean) throws OptimisticLockException { - serverMgr.getPrimaryServer().markAsDirty(bean); - } - - /** - * Saves the bean using an update. If you know you are updating a bean then it is preferrable to - * use this update() method rather than save(). - *

- * Stateless updates: Note that the bean does not have to be previously fetched to call - * update().You can create a new instance and set some of its properties programmatically for via - * JSON/XML marshalling etc. This is described as a 'stateless update'. - *

- *

- * Optimistic Locking: Note that if the version property is not set when update() is - * called then no optimistic locking is performed (internally ConcurrencyMode.NONE is used). - *

- *

- * {@link ServerConfig#setUpdatesDeleteMissingChildren(boolean)}: When cascade saving to a - * OneToMany or ManyToMany the updatesDeleteMissingChildren setting controls if any other children - * that are in the database but are not in the collection are deleted. - *

- *

- * {@link ServerConfig#setUpdateChangesOnly(boolean)}: The updateChangesOnly setting - * controls if only the changed properties are included in the update or if all the loaded - * properties are included instead. - *

- * - *
{@code
-   * 
-   *   // A 'stateless update' example
-   *   Customer customer = new Customer();
-   *   customer.setId(7);
-   *   customer.setName("ModifiedNameNoOCC");
-   *   ebeanServer.update(customer);
-   * 
-   * }
- * - * @see ServerConfig#setUpdatesDeleteMissingChildren(boolean) - * @see ServerConfig#setUpdateChangesOnly(boolean) - */ - public static void update(Object bean) throws OptimisticLockException { - serverMgr.getPrimaryServer().update(bean); - } - - /** - * Update the beans in the collection. - */ - public static void update(Collection beans) throws OptimisticLockException { - serverMgr.getPrimaryServer().update(beans); - } - - /** - * Save all the beans from an Iterator. - */ - public static int save(Iterator iterator) throws OptimisticLockException { - return serverMgr.getPrimaryServer().save(iterator); - } - - /** - * Save all the beans from a Collection. - */ - public static int save(Collection beans) throws OptimisticLockException { - return serverMgr.getPrimaryServer().save(beans); - } - - /** - * Delete the associations (from the intersection table) of a ManyToMany given - * the owner bean and the propertyName of the ManyToMany collection. - *

- * Typically these deletions occur automatically when persisting a ManyToMany - * collection and this provides a way to invoke those deletions directly. - *

- * - * @return the number of associations deleted (from the intersection table). - */ - public static int deleteManyToManyAssociations(Object ownerBean, String propertyName) { - return serverMgr.getPrimaryServer().deleteManyToManyAssociations(ownerBean, propertyName); - } - - /** - * Save the associations of a ManyToMany given the owner bean and the - * propertyName of the ManyToMany collection. - *

- * Typically the saving of these associations (inserting into the intersection - * table) occurs automatically when persisting a ManyToMany. This provides a - * way to invoke those insertions directly. - *

- *

- * You can use this when the collection is new and in this case all the - * entries in the collection are treated as additions are result in inserts - * into the intersection table. - *

- */ - public static void saveManyToManyAssociations(Object ownerBean, String propertyName) { - serverMgr.getPrimaryServer().saveManyToManyAssociations(ownerBean, propertyName); - } - - /** - * Save the associated collection or bean given the property name. - *

- * This is similar to performing a save cascade on a specific property - * manually/programmatically. - *

- *

- * Note that you can turn on/off cascading for a transaction via - * {@link Transaction#setPersistCascade(boolean)} - *

- * - * @param ownerBean - * the bean instance holding the property we want to save - * @param propertyName - * the property we want to save - */ - public static void saveAssociation(Object ownerBean, String propertyName) { - serverMgr.getPrimaryServer().saveAssociation(ownerBean, propertyName); - } - - /** - * Delete the bean. - *

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

- */ - public static void delete(Object bean) throws OptimisticLockException { - serverMgr.getPrimaryServer().delete(bean); - } - - /** - * Delete the bean given its type and id. - */ - public static int delete(Class beanType, Object id) { - return serverMgr.getPrimaryServer().delete(beanType, id); - } - - /** - * Delete several beans given their type and id values. - */ - public static void delete(Class beanType, Collection ids) { - serverMgr.getPrimaryServer().delete(beanType, ids); - } - - /** - * Delete all the beans from an Iterator. - */ - public static int delete(Iterator it) throws OptimisticLockException { - return serverMgr.getPrimaryServer().delete(it); - } - - /** - * Delete all the beans from a Collection. - */ - public static int delete(Collection c) throws OptimisticLockException { - return delete(c.iterator()); - } - - /** - * Refresh the values of a bean. - *

- * 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) { - serverMgr.getPrimaryServer().refresh(bean); - } - - /** - * Refresh a 'many' property of a bean. - * - *
{@code
-   *
-   *   Order order = ...;
-   *   ...
-   *   // refresh the order details...
-   *   Ebean.refreshMany(order, "details");
-   *
-   * }
- * - * @param bean - * the entity bean containing the List Set or Map to refresh. - * @param manyPropertyName - * the property name of the List Set or Map to refresh. - */ - public static void refreshMany(Object bean, String manyPropertyName) { - serverMgr.getPrimaryServer().refreshMany(bean, manyPropertyName); - } - - /** - * Get a reference object. - *

- * This is sometimes described as a proxy (with lazy loading). - *

- * - *
{@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();
-   *
-   * }
- * - * @param beanType - * the type of entity bean - * @param id - * the id value - */ - public static T getReference(Class beanType, Object id) { - return serverMgr.getPrimaryServer().getReference(beanType, id); - } - - /** - * Sort the list 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 = Ebean.find(Order.class)
-   *     .fetch("customer")
-   *     .orderBy("id")
-   *     .findList();
-   * 
-   *   // sort by customer name ascending, then by order shipDate
-   *   // ... then by the order status descending
-   *   Ebean.sort(list, "customer.name, shipDate, status desc");
-   * 
-   *   // sort by customer name descending (with nulls low)
-   *   // ... then by the order id
-   *   Ebean.sort(list, "customer.name desc nullsLow, id");
-   * 
-   * }
- * - * @param list - * the list of entity beans - * @param sortByClause - * the properties to sort the list by - */ - public static void sort(List list, String sortByClause) { - serverMgr.getPrimaryServer().sort(list, sortByClause); - } - - /** - * Find a bean using its unique id. This will not use caching. - * - *
{@code
-   *   // Fetch order 1
-   *   Order order = Ebean.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 = Ebean.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");
-   * 
-   *   // traverse the object graph...
-   * 
-   *   Order order = query.findUnique();
-   *   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 static T find(Class beanType, Object id) { - return serverMgr.getPrimaryServer().find(beanType, id); - } - - /** - * 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 static SqlQuery createSqlQuery(String sql) { - return serverMgr.getPrimaryServer().createSqlQuery(sql); - } - - /** - * Create a named sql query. - *

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

- * - * @param namedQuery - * the name of the query - */ - public static SqlQuery createNamedSqlQuery(String namedQuery) { - return serverMgr.getPrimaryServer().createNamedSqlQuery(namedQuery); - } - - /** - * 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 static SqlUpdate createSqlUpdate(String sql) { - return serverMgr.getPrimaryServer().createSqlUpdate(sql); - } - - /** - * Create a CallableSql to execute a given stored procedure. - * - * @see CallableSql - */ - public static CallableSql createCallableSql(String sql) { - return serverMgr.getPrimaryServer().createCallableSql(sql); - } - - /** - * Create a named sql update. - *

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

- * - *
{@code
-   *
-   *   // Use a namedQuery
-   *   UpdateSql update = Ebean.createNamedSqlUpdate("update.topic.count");
-   * 
-   *   update.setParameter("count", 1);
-   *   update.setParameter("topicId", 50);
-   * 
-   *   int modifiedCount = update.execute();
-   *
-   * }
- */ - public static SqlUpdate createNamedSqlUpdate(String namedQuery) { - return serverMgr.getPrimaryServer().createNamedSqlUpdate(namedQuery); - } - - /** - * Return a named Query that will have defined fetch paths, predicates etc. - *

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

- * - *
{@code
-   *
-   *   // example
-   *   Query query = Ebean.createNamedQuery(Order.class, "new.for.customer");
-   *   query.setParameter("customerId", 23);
-   *   List newOrders = query.findList();
-   *
-   * }
- * - * @param beanType - * the class of entity to be fetched - * @param namedQuery - * the name of the query - */ - public static Query createNamedQuery(Class beanType, String namedQuery) { - - return serverMgr.getPrimaryServer().createNamedQuery(beanType, namedQuery); - } - - /** - * Create a query using the query language. - *

- * Note that you are allowed to add additional clauses using where() as well - * as use fetch() and setOrderBy() after the query has been created. - *

- *

- * Note that this method signature used to map to named queries and that has - * moved to {@link #createNamedQuery(Class, String)}. - *

- * - *
{@code
-   * 
-   *   String q = "find order fetch details where status = :st";
-   * 
-   *   List newOrders = Ebean.>findOrder.class, q)
-   *     .setParameter("st", Order.Status.NEW)
-   *     .findList();
-   *
-   * }
- * - * @param query - * the object query - */ - public static Query createQuery(Class beanType, String query) { - return serverMgr.getPrimaryServer().createQuery(beanType, query); - } - - /** - * 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 = Ebean.createNamedUpdate(Topic.class, "setPostCount");
-   *   update.setParameter("postCount", 10);
-   *   update.setParameter("id", 3);
-   * 
-   *   int rows = update.execute();
-   *   System.out.println("rows updated: " + rows);
-   *
-   * }
- */ - public static Update createNamedUpdate(Class beanType, String namedUpdate) { - - return serverMgr.getPrimaryServer().createNamedUpdate(beanType, namedUpdate); - } - - /** - * 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 = Ebean.createUpdate(Topic.class, updStatement);
-   * 
-   *   update.set("pc", 9);
-   *   update.set("id", 3);
-   * 
-   *   int rows = update.execute();
-   *   System.out.println("rows updated:" + rows);
-   *
-   * }
- */ - public static Update createUpdate(Class beanType, String ormUpdate) { - - return serverMgr.getPrimaryServer().createUpdate(beanType, ormUpdate); - } - - /** - * Create a CsvReader for a given beanType. - */ - public static CsvReader createCsvReader(Class beanType) { - - return serverMgr.getPrimaryServer().createCsvReader(beanType); - } - - /** - * 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 additionally fetching the customer, details and details.product
-   *   // name.
-   * 
-   *   Order order = Ebean.find(Order.class)
-   *     .fetch("customer")
-   *     .fetch("details")
-   *     .fetch("detail.product", "name")
-   *     .setId(2)
-   *     .findUnique();
-   * 
-   *   // Find order 2 additionally fetching the customer, details and details.product
-   *   // name.
-   *   // Note: same query as above but using the query language
-   *   // Note: using a named query would be preferred practice
-   * 
-   *   String oql = "find order fetch customer fetch details fetch details.product (name) where id = :orderId ";
-   * 
-   *   Query query = Ebean.find(Order.class);
-   *   query.setQuery(oql);
-   *   query.setParameter("orderId", 2);
-   * 
-   *   Order order = query.findUnique();
-   * 
-   *   // Using a named query
-   *   Query query = Ebean.find(Order.class, "with.details");
-   *   query.setParameter("orderId", 2);
-   * 
-   *   Order order = query.findUnique();
-   * 
-   * }
- * - * @param beanType - * the class of entity to be fetched - * @return A ORM Query object for this beanType - */ - public static Query createQuery(Class beanType) { - - return serverMgr.getPrimaryServer().createQuery(beanType); - } - - /** - * Create a query for a type of entity bean. - *

- * This is actually the same as {@link #createQuery(Class)}. The reason it - * exists is that people used to JPA will probably be looking for a - * createQuery method (the same as entityManager). - *

- * - * @param beanType - * the type of entity bean to find - * @return A ORM Query object for this beanType - */ - public static Query find(Class beanType) { - - return serverMgr.getPrimaryServer().find(beanType); - } - - /** - * 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 static Filter filter(Class beanType) { - return serverMgr.getPrimaryServer().filter(beanType); - } - - /** - * 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(). - *

- * If you wish to execute a Sql Select natively then you should use the - * FindByNativeSql object. - *

- *

- * 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 = Ebean.createSqlUpdate(s);
-   * 
-   *   update.setParameter("id", 1);
-   *   update.setParameter("count", 50);
-   * 
-   *   int modifiedCount = Ebean.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 SqlUpdate - * @see CallableSql - * @see Ebean#execute(CallableSql) - */ - public static int execute(SqlUpdate sqlUpdate) { - return serverMgr.getPrimaryServer().execute(sqlUpdate); - } - - /** - * For making calls to stored procedures. - *

- * Example: - *

- * - *
{@code
-   *
-   *   String sql = "{call sp_order_modify(?,?,?)}";
-   * 
-   *   CallableSql cs = Ebean.createCallableSql(sql);
-   *   cs.setParameter(1, 27);
-   *   cs.setParameter(2, "SHIPPED");
-   *   cs.registerOut(3, Types.INTEGER);
-   * 
-   *   Ebean.execute(cs);
-   * 
-   *   // read the out parameter
-   *   Integer returnValue = (Integer) cs.getObject(3);
-   *
-   * }
- * - * @see CallableSql - * @see Ebean#execute(SqlUpdate) - */ - public static int execute(CallableSql callableSql) { - return serverMgr.getPrimaryServer().execute(callableSql); - } - - /** - * Execute a TxRunnable in a Transaction with an explicit scope. - *

- * The scope can control the transaction type, isolation and rollback - * semantics. - *

- * - *
{@code
-   *
-   *   // set specific transactional scope settings
-   *   TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
-   *
-   *   Ebean.execute(scope, new TxRunnable() {
-   * 	   public void run() {
-   * 		   User u1 = Ebean.find(User.class, 1);
-   * 		   ...
-   * 	   }
-   *   });
-   *
-   * }
- */ - public static void execute(TxScope scope, TxRunnable r) { - serverMgr.getPrimaryServer().execute(scope, r); - } - - /** - * Execute a TxRunnable in a Transaction with the default scope. - *

- * The default scope runs with REQUIRED and by default will rollback on any - * exception (checked or runtime). - *

- * - *
{@code
-   *
-   *   Ebean.execute(new TxRunnable() {
-   *     public void run() {
-   *       User u1 = Ebean.find(User.class, 1);
-   *       User u2 = Ebean.find(User.class, 2);
-   * 
-   *       u1.setName("u1 mod");
-   *       u2.setName("u2 mod");
-   * 
-   *       Ebean.save(u1);
-   *       Ebean.save(u2);
-   *     }
-   *   });
-   *
-   * }
- */ - public static void execute(TxRunnable r) { - serverMgr.getPrimaryServer().execute(r); - } - - /** - * Execute a TxCallable in a Transaction with an explicit scope. - *

- * The scope can control the transaction type, isolation and rollback - * semantics. - *

- * - *
{@code
-   *
-   *   // set specific transactional scope settings
-   *   TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
-   *
-   *   Ebean.execute(scope, new TxCallable() {
-   * 	   public String call() {
-   * 		   User u1 = Ebean.find(User.class, 1);
-   * 		   ...
-   * 		   return u1.getEmail();
-   * 	   }
-   *   });
-   *
-   * }
- * - */ - public static T execute(TxScope scope, TxCallable c) { - return serverMgr.getPrimaryServer().execute(scope, c); - } - - /** - * Execute a TxCallable in a Transaction with the default scope. - *

- * 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
-   *
-   *   Ebean.execute(new TxCallable() {
-   *     public String call() {
-   *       User u1 = Ebean.find(User.class, 1);
-   *       User u2 = Ebean.find(User.class, 2);
-   * 
-   *       u1.setName("u1 mod");
-   *       u2.setName("u2 mod");
-   * 
-   *       Ebean.save(u1);
-   *       Ebean.save(u2);
-   * 
-   *       return u1.getEmail();
-   *     }
-   *   });
-   *
-   * }
- */ - public static T execute(TxCallable c) { - return serverMgr.getPrimaryServer().execute(c); - } - - /** - * 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. - *

- * If you use Ebean.execute(UpdateSql) then the table modification information - * is automatically deduced and you do not need to call this method yourself. - *

- *

- * 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 inserts - * true if rows where inserted into the table - * @param updates - * true if rows on the table where updated - * @param deletes - * true if rows on the table where deleted - */ - public static void externalModification(String tableName, boolean inserts, boolean updates, - boolean deletes) { - - serverMgr.getPrimaryServer().externalModification(tableName, inserts, updates, deletes); - } - - /** - * Return the BeanState for a given entity bean. - *

- * This will return null if the bean is not an enhanced entity bean. - *

- */ - public static BeanState getBeanState(Object bean) { - return serverMgr.getPrimaryServer().getBeanState(bean); - } - - /** - * Return the manager of the server cache ("L2" cache). - * - */ - public static ServerCacheManager getServerCacheManager() { - return serverMgr.getPrimaryServer().getServerCacheManager(); - } - - /** - * Return the BackgroundExecutor service for asynchronous processing of - * queries. - */ - public static BackgroundExecutor getBackgroundExecutor() { - return serverMgr.getPrimaryServer().getBackgroundExecutor(); - } - - /** - * Run the cache warming queries on all bean types that have one defined for - * the default/primary EbeanServer. - *

- * A cache warming query can be defined via {@link CacheStrategy}. - *

- */ - public static void runCacheWarming() { - serverMgr.getPrimaryServer().runCacheWarming(); - } - - /** - * Run the cache warming query for a specific bean type for the - * default/primary EbeanServer. - *

- * A cache warming query can be defined via {@link CacheStrategy}. - *

- */ - public static void runCacheWarming(Class beanType) { - - serverMgr.getPrimaryServer().runCacheWarming(beanType); - } - - /** - * Return the JsonContext for reading/writing JSON. - */ - public static JsonContext json() { - return serverMgr.getPrimaryServer().json(); - } - - /** - * Return the JsonContext for reading/writing JSON. - * @deprecated Please use #json instead. - */ - public static JsonContext createJsonContext() { - return json(); - } - -} +package com.avaje.ebean; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.persistence.OptimisticLockException; +import javax.persistence.PersistenceException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.avaje.ebean.annotation.CacheStrategy; +import com.avaje.ebean.cache.ServerCacheManager; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.text.csv.CsvReader; +import com.avaje.ebean.text.json.JsonContext; + +/** + * This Ebean object is effectively a singleton that holds a map of registered + * {@link EbeanServer}s. It additionally provides a convenient way to use the + * 'default/primary' EbeanServer. + *

+ * If you are using a Dependency Injection framework such as + * Spring or Guice you will probably + * NOT use this Ebean singleton object. Instead you will + * configure and construct EbeanServer instances using {@link ServerConfig} and + * {@link EbeanServerFactory} and inject those EbeanServer instances into your + * data access objects. + *

+ *

+ * In documentation "Ebean singleton" refers to this object. + *

+ *
    + *
  • There is one EbeanServer per Database (javax.sql.DataSource).
  • + *
  • EbeanServers can be 'registered' with the Ebean singleton (put into its + * map). Registered EbeanServer's can later be retrieved via + * {@link #getServer(String)}.
  • + *
  • One EbeanServer can be referred to as the 'default' EbeanServer. For + * convenience, the Ebean singleton (this object) provides methods such as + * {@link #find(Class)} that proxy through to the 'default' EbeanServer. This + * can be useful for applications that use a single database.
  • + *
+ * + *

+ * For developer convenience Ebean has static methods that proxy through to the + * methods on the 'default' EbeanServer. These methods are provided for + * developers who are mostly using a single database. Many developers will be + * able to use the methods on Ebean rather than get a EbeanServer. + *

+ *

+ * EbeanServers can be created and used without ever needing or using the Ebean + * singleton. Refer to {@link ServerConfig#setRegister(boolean)}. + *

+ *

+ * You can either programmatically create/register EbeanServers via + * {@link EbeanServerFactory} or they can automatically be created and + * registered when you first use the Ebean singleton. When EbeanServers are + * created automatically they are configured using information in the + * ebean.properties file. + *

+ * + *
{@code
+ *
+ *   // fetch shipped orders (and also their customer)
+ *   List list = Ebean.find(Order.class)
+ * 	  .fetch("customer")
+ * 	  .where()
+ * 	  .eq("status.code", Order.Status.SHIPPED)
+ * 	  .findList();
+ *
+ *   // read/use the order list ...
+ *   for (Order order : list) {
+ * 	   Customer customer = order.getCustomer();
+ * 	   ...
+ *   }
+ *
+ * }
+ * + *
{@code
+ *
+ *   // fetch order 10, modify and save
+ *   Order order = Ebean.find(Order.class, 10);
+ * 
+ *   OrderStatus shipped = Ebean.getReference(OrderStatus.class,"SHIPPED");
+ *   order.setStatus(shipped);
+ *   order.setShippedDate(shippedDate);
+ *   ...
+ * 
+ *   // implicitly creates a transaction and commits
+ *   Ebean.save(order);
+ *
+ * }
+ * + *

+ * When you have multiple databases and need access to a specific one the + * {@link #getServer(String)} method provides access to the EbeanServer for that + * specific database. + *

+ * + *
 {@code
+ *
+ *   // Get access to the Human Resources EbeanServer/Database
+ *   EbeanServer hrDb = Ebean.getServer("hr");
+ * 
+ * 
+ *   // fetch contact 3 from the HR database
+ *   Contact contact = hrDb.find(Contact.class, 3);
+ * 
+ *   contact.setName("I'm going to change");
+ *   ...
+ * 
+ *   // save the contact back to the HR database
+ *   hrDb.save(contact);
+ *
+ * }
+ */ +public final class Ebean { + private static final Logger logger = LoggerFactory.getLogger(Ebean.class); + + /** + * Manages creation and cache of EbeanServers. + */ + private static final Ebean.ServerManager serverMgr = new Ebean.ServerManager(); + + /** + * Helper class for managing fast and safe access and creation of + * EbeanServers. + */ + private static final class ServerManager { + + /** + * Cache for fast concurrent read access. + */ + private final ConcurrentHashMap concMap = new ConcurrentHashMap(); + + /** + * Cache for synchronized read, creation and put. Protected by the monitor + * object. + */ + private final HashMap syncMap = new HashMap(); + + private final Object monitor = new Object(); + + /** + * The 'default/primary' EbeanServer. + */ + private EbeanServer primaryServer; + + private ServerManager() { + + try { + // skipDefaultServer is set by EbeanServerFactory + // ... when it is creating the primaryServer + if (PrimaryServer.isSkip()) { + // primary server being created by EbeanServerFactory + // ... so we should not try and create it here + logger.debug("PrimaryServer.isSkip()"); + + } else { + // look to see if there is a default server defined + String primaryName = PrimaryServer.getPrimaryServerName(); + logger.debug("primaryName:" + primaryName); + if (primaryName != null && primaryName.trim().length() > 0) { + primaryServer = getWithCreate(primaryName.trim()); + } + } + } catch (RuntimeException e) { + logger.error("Error trying to create the default EbeanServer", e); + throw e; + } + } + + private EbeanServer getPrimaryServer() { + if (primaryServer == null) { + String msg = "The default EbeanServer has not been defined?"; + msg += " This is normally set via the ebean.datasource.default property."; + msg += " Otherwise it should be registered programatically via registerServer()"; + throw new PersistenceException(msg); + } + return primaryServer; + } + + private EbeanServer get(String name) { + if (name == null || name.length() == 0) { + return primaryServer; + } + // non-synchronized read + EbeanServer server = concMap.get(name); + if (server != null) { + return server; + } + // synchronized read, create and put + return getWithCreate(name); + } + + /** + * Synchronized read, create and put of EbeanServers. + */ + private EbeanServer getWithCreate(String name) { + + synchronized (monitor) { + + EbeanServer server = syncMap.get(name); + if (server == null) { + // register when creating server this way + server = EbeanServerFactory.create(name); + register(server, false); + } + return server; + } + } + + /** + * Register a server so we can get it by its name. + */ + private void register(EbeanServer server, boolean isPrimaryServer) { + registerWithName(server.getName(), server, isPrimaryServer); + } + + private void registerWithName(String name, EbeanServer server, boolean isPrimaryServer) { + synchronized (monitor) { + concMap.put(name, server); + syncMap.put(name, server); + if (isPrimaryServer) { + primaryServer = server; + } + } + } + + } + + private Ebean() { + } + + /** + * Get the EbeanServer for a given DataSource. If name is null this will + * return the 'default' EbeanServer. + *

+ * This is provided to access EbeanServer for databases other than the + * 'default' database. EbeanServer also provides more control over + * transactions and the ability to use transactions created externally to + * Ebean. + *

+ * + *
{@code
+   * // use the "hr" database
+   * EbeanServer hrDatabase = Ebean.getServer("hr");
+   * 
+   * Person person = hrDatabase.find(Person.class, 10);
+   * }
+ * + * @param name + * the name of the server, use null for the 'default server' + */ + public static EbeanServer getServer(String name) { + return serverMgr.get(name); + } + + /** + * Return the ExpressionFactory from the default server. + *

+ * The ExpressionFactory is used internally by the query and ExpressionList to + * build the WHERE and HAVING clauses. Alternatively you can use the + * ExpressionFactory directly to create expressions to add to the query where + * clause. + *

+ *

+ * Alternatively you can use the {@link Expr} as a shortcut to the + * ExpressionFactory of the 'Default' EbeanServer. + *

+ *

+ * You generally need to the an ExpressionFactory (or {@link Expr}) to build + * an expression that uses OR like Expression e = Expr.or(..., ...); + *

+ */ + public static ExpressionFactory getExpressionFactory() { + return serverMgr.getPrimaryServer().getExpressionFactory(); + } + + /** + * Register the server with this Ebean singleton. Specify if the registered + * server is the primary/default server. + */ + public static void register(EbeanServer server, boolean isPrimaryServer) { + serverMgr.register(server, isPrimaryServer); + } + + /** + * Backdoor for registering a mock implementation of EbeanServer as the default server. + */ + protected static EbeanServer mock(String name, EbeanServer server, boolean isPrimaryServer) { + EbeanServer originalPrimaryServer = serverMgr.primaryServer; + serverMgr.registerWithName(name, server, isPrimaryServer); + return originalPrimaryServer; + } + + /** + * Return the next identity value for a given bean type. + *

+ * This will only work when a IdGenerator is on this bean type such as a DB + * sequence or UUID. + *

+ *

+ * For DB's supporting getGeneratedKeys and sequences such as Oracle10 you do + * not need to use this method generally. It is made available for more + * complex cases where it is useful to get an ID prior to some processing. + *

+ */ + public static Object nextId(Class beanType) { + return serverMgr.getPrimaryServer().nextId(beanType); + } + + /** + * Start a new explicit transaction. + *

+ * 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)
+   *   Ebean.beginTransaction();
+   *   try {
+   * 	   Order order = Ebean.find(Order.class,10); ...
+   *
+   * 	   Ebean.save(order);
+   * 
+   * 	   Ebean.commitTransaction();
+   * 
+   *   } finally {
+   * 	   // rollback if we didn't commit
+   * 	   // i.e. an exception occurred before commitTransaction().
+   * 	   Ebean.endTransaction();
+   *   }
+   *
+   * }
+ * + *

+ * If you want to externalise the transaction management then you should be + * able to do this via EbeanServer. Specifically with EbeanServer you can pass + * the transaction to the various find() and save() execute() methods. This + * gives you the ability to create the transactions yourself externally from + * Ebean and pass those transactions through to the various methods available + * on EbeanServer. + *

+ */ + public static Transaction beginTransaction() { + return serverMgr.getPrimaryServer().beginTransaction(); + } + + /** + * Start a transaction additionally specifying the isolation level. + * + * @param isolation + * the Transaction isolation level + * + */ + public static Transaction beginTransaction(TxIsolation isolation) { + return serverMgr.getPrimaryServer().beginTransaction(isolation); + } + + /** + * Start a transaction typically specifying REQUIRES_NEW or REQUIRED semantics. + * + *

+ * Note that this provides an try finally alternative to using {@link #execute(TxScope, TxCallable)} or + * {@link #execute(TxScope, TxRunnable)}. + *

+ * + *

REQUIRES_NEW example:

+ *
{@code
+   * // Start a new transaction. If there is a current transaction
+   * // suspend it until this transaction ends
+   * Transaction txn = Ebean.beginTransaction(TxScope.requiresNew());
+   * try {
+   *
+   *   ...
+   *
+   *   // commit the transaction
+   *   txn.commit();
+   *
+   * } finally {
+   *   // end this transaction which:
+   *   //  A) will rollback transaction if it has not been committed already
+   *   //  B) will restore a previously suspended transaction
+   *   txn.end();
+   * }
+   *
+   * }
+ * + *

REQUIRED example:

+ *
{@code
+   *
+   * // start a new transaction if there is not a current transaction
+   * Transaction txn = Ebean.beginTransaction(TxScope.required());
+   * try {
+   *
+   *   ...
+   *
+   *   // commit the transaction if it was created or
+   *   // do nothing if there was already a current transaction
+   *   txn.commit();
+   *
+   * } finally {
+   *   // end this transaction which will rollback the transaction
+   *   // if it was created for this try finally scope and has not
+   *   // already been committed
+   *   txn.end();
+   * }
+   *
+   * }
+ */ + public static Transaction beginTransaction(TxScope scope){ + return serverMgr.getPrimaryServer().beginTransaction(scope); + } + + /** + * Returns the current transaction or null if there is no current transaction + * in scope. + */ + public static Transaction currentTransaction() { + return serverMgr.getPrimaryServer().currentTransaction(); + } + + /** + * Register a TransactionCallback on the currently active transaction. + *

+ * If there is no currently active transaction then a PersistenceException is thrown. + * + * @param transactionCallback the transaction callback to be registered with the current transaction + * + * @throws PersistenceException if there is no currently active transaction + */ + public static void register(TransactionCallback transactionCallback) throws PersistenceException { + serverMgr.getPrimaryServer().register(transactionCallback); + } + + /** + * Commit the current transaction. + */ + public static void commitTransaction() { + serverMgr.getPrimaryServer().commitTransaction(); + } + + /** + * Rollback the current transaction. + */ + public static void rollbackTransaction() { + serverMgr.getPrimaryServer().rollbackTransaction(); + } + + /** + * If the current transaction has already been committed do nothing otherwise + * rollback the transaction. + *

+ * Useful to put in a finally block to ensure the transaction is ended, rather + * than a rollbackTransaction() in each catch block. + *

+ *

+ * Code example: + *

+ * + *
{@code
+   *   Ebean.beginTransaction();
+   *   try {
+   *     // do some fetching and or persisting
+   *
+   *     // commit at the end
+   *     Ebean.commitTransaction();
+   * 
+   *   } finally {
+   *     // if commit didn't occur then rollback the transaction
+   *     Ebean.endTransaction();
+   *   }
+   * }
+ */ + public static void endTransaction() { + serverMgr.getPrimaryServer().endTransaction(); + } + + /** + * Return a map of the differences between two objects of the same type. + *

+ * When null is passed in for b, then the 'OldValues' of a is used for the + * difference comparison. + *

+ */ + public static Map diff(Object a, Object b) { + return serverMgr.getPrimaryServer().diff(a, b); + } + + /** + * 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 static void save(Object bean) throws OptimisticLockException { + serverMgr.getPrimaryServer().save(bean); + } + + /** + * Insert the bean. This is useful when you set the Id property on a bean and + * want to explicitly insert it. + */ + public static void insert(Object bean) { + serverMgr.getPrimaryServer().insert(bean); + } + + /** + * Insert a collection of beans. + */ + public static void insert(Collection beans) { + serverMgr.getPrimaryServer().insert(beans); + } + + /** + * Marks the entity bean as dirty. + *

+ * This is used so that when a bean that is otherwise unmodified is updated with the version + * property updated. + *

+ * An unmodified bean that is saved or updated is normally skipped and this marks the bean as + * dirty so that it is not skipped. + * + *

{@code
+   * 
+   *   Customer customer = Ebean.find(Customer, id);
+   * 
+   *   // mark the bean as dirty so that a save() or update() will
+   *   // increment the version property
+   *   Ebean.markAsDirty(customer);
+   *   Ebean.save(customer);
+   * 
+   * }
+ */ + public static void markAsDirty(Object bean) throws OptimisticLockException { + serverMgr.getPrimaryServer().markAsDirty(bean); + } + + /** + * Saves the bean using an update. If you know you are updating a bean then it is preferrable to + * use this update() method rather than save(). + *

+ * Stateless updates: Note that the bean does not have to be previously fetched to call + * update().You can create a new instance and set some of its properties programmatically for via + * JSON/XML marshalling etc. This is described as a 'stateless update'. + *

+ *

+ * Optimistic Locking: Note that if the version property is not set when update() is + * called then no optimistic locking is performed (internally ConcurrencyMode.NONE is used). + *

+ *

+ * {@link ServerConfig#setUpdatesDeleteMissingChildren(boolean)}: When cascade saving to a + * OneToMany or ManyToMany the updatesDeleteMissingChildren setting controls if any other children + * that are in the database but are not in the collection are deleted. + *

+ *

+ * {@link ServerConfig#setUpdateChangesOnly(boolean)}: The updateChangesOnly setting + * controls if only the changed properties are included in the update or if all the loaded + * properties are included instead. + *

+ * + *
{@code
+   * 
+   *   // A 'stateless update' example
+   *   Customer customer = new Customer();
+   *   customer.setId(7);
+   *   customer.setName("ModifiedNameNoOCC");
+   *   ebeanServer.update(customer);
+   * 
+   * }
+ * + * @see ServerConfig#setUpdatesDeleteMissingChildren(boolean) + * @see ServerConfig#setUpdateChangesOnly(boolean) + */ + public static void update(Object bean) throws OptimisticLockException { + serverMgr.getPrimaryServer().update(bean); + } + + /** + * Update the beans in the collection. + */ + public static void update(Collection beans) throws OptimisticLockException { + serverMgr.getPrimaryServer().update(beans); + } + + /** + * Save all the beans from an Iterator. + */ + public static int save(Iterator iterator) throws OptimisticLockException { + return serverMgr.getPrimaryServer().save(iterator); + } + + /** + * Save all the beans from a Collection. + */ + public static int save(Collection beans) throws OptimisticLockException { + return serverMgr.getPrimaryServer().save(beans); + } + + /** + * Delete the associations (from the intersection table) of a ManyToMany given + * the owner bean and the propertyName of the ManyToMany collection. + *

+ * Typically these deletions occur automatically when persisting a ManyToMany + * collection and this provides a way to invoke those deletions directly. + *

+ * + * @return the number of associations deleted (from the intersection table). + */ + public static int deleteManyToManyAssociations(Object ownerBean, String propertyName) { + return serverMgr.getPrimaryServer().deleteManyToManyAssociations(ownerBean, propertyName); + } + + /** + * Save the associations of a ManyToMany given the owner bean and the + * propertyName of the ManyToMany collection. + *

+ * Typically the saving of these associations (inserting into the intersection + * table) occurs automatically when persisting a ManyToMany. This provides a + * way to invoke those insertions directly. + *

+ *

+ * You can use this when the collection is new and in this case all the + * entries in the collection are treated as additions are result in inserts + * into the intersection table. + *

+ */ + public static void saveManyToManyAssociations(Object ownerBean, String propertyName) { + serverMgr.getPrimaryServer().saveManyToManyAssociations(ownerBean, propertyName); + } + + /** + * Save the associated collection or bean given the property name. + *

+ * This is similar to performing a save cascade on a specific property + * manually/programmatically. + *

+ *

+ * Note that you can turn on/off cascading for a transaction via + * {@link Transaction#setPersistCascade(boolean)} + *

+ * + * @param ownerBean + * the bean instance holding the property we want to save + * @param propertyName + * the property we want to save + */ + public static void saveAssociation(Object ownerBean, String propertyName) { + serverMgr.getPrimaryServer().saveAssociation(ownerBean, propertyName); + } + + /** + * Delete the bean. + *

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

+ */ + public static void delete(Object bean) throws OptimisticLockException { + serverMgr.getPrimaryServer().delete(bean); + } + + /** + * Delete the bean given its type and id. + */ + public static int delete(Class beanType, Object id) { + return serverMgr.getPrimaryServer().delete(beanType, id); + } + + /** + * Delete several beans given their type and id values. + */ + public static void delete(Class beanType, Collection ids) { + serverMgr.getPrimaryServer().delete(beanType, ids); + } + + /** + * Delete all the beans from an Iterator. + */ + public static int delete(Iterator it) throws OptimisticLockException { + return serverMgr.getPrimaryServer().delete(it); + } + + /** + * Delete all the beans from a Collection. + */ + public static int delete(Collection c) throws OptimisticLockException { + return delete(c.iterator()); + } + + /** + * Refresh the values of a bean. + *

+ * 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) { + serverMgr.getPrimaryServer().refresh(bean); + } + + /** + * Refresh a 'many' property of a bean. + * + *
{@code
+   *
+   *   Order order = ...;
+   *   ...
+   *   // refresh the order details...
+   *   Ebean.refreshMany(order, "details");
+   *
+   * }
+ * + * @param bean + * the entity bean containing the List Set or Map to refresh. + * @param manyPropertyName + * the property name of the List Set or Map to refresh. + */ + public static void refreshMany(Object bean, String manyPropertyName) { + serverMgr.getPrimaryServer().refreshMany(bean, manyPropertyName); + } + + /** + * Get a reference object. + *

+ * This is sometimes described as a proxy (with lazy loading). + *

+ * + *
{@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();
+   *
+   * }
+ * + * @param beanType + * the type of entity bean + * @param id + * the id value + */ + public static T getReference(Class beanType, Object id) { + return serverMgr.getPrimaryServer().getReference(beanType, id); + } + + /** + * Sort the list 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 = Ebean.find(Order.class)
+   *     .fetch("customer")
+   *     .orderBy("id")
+   *     .findList();
+   * 
+   *   // sort by customer name ascending, then by order shipDate
+   *   // ... then by the order status descending
+   *   Ebean.sort(list, "customer.name, shipDate, status desc");
+   * 
+   *   // sort by customer name descending (with nulls low)
+   *   // ... then by the order id
+   *   Ebean.sort(list, "customer.name desc nullsLow, id");
+   * 
+   * }
+ * + * @param list + * the list of entity beans + * @param sortByClause + * the properties to sort the list by + */ + public static void sort(List list, String sortByClause) { + serverMgr.getPrimaryServer().sort(list, sortByClause); + } + + /** + * Find a bean using its unique id. This will not use caching. + * + *
{@code
+   *   // Fetch order 1
+   *   Order order = Ebean.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 = Ebean.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");
+   * 
+   *   // traverse the object graph...
+   * 
+   *   Order order = query.findUnique();
+   *   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 static T find(Class beanType, Object id) { + return serverMgr.getPrimaryServer().find(beanType, id); + } + + /** + * 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 static SqlQuery createSqlQuery(String sql) { + return serverMgr.getPrimaryServer().createSqlQuery(sql); + } + + /** + * Create a named sql query. + *

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

+ * + * @param namedQuery + * the name of the query + */ + public static SqlQuery createNamedSqlQuery(String namedQuery) { + return serverMgr.getPrimaryServer().createNamedSqlQuery(namedQuery); + } + + /** + * 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 static SqlUpdate createSqlUpdate(String sql) { + return serverMgr.getPrimaryServer().createSqlUpdate(sql); + } + + /** + * Create a CallableSql to execute a given stored procedure. + * + * @see CallableSql + */ + public static CallableSql createCallableSql(String sql) { + return serverMgr.getPrimaryServer().createCallableSql(sql); + } + + /** + * Create a named sql update. + *

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

+ * + *
{@code
+   *
+   *   // Use a namedQuery
+   *   UpdateSql update = Ebean.createNamedSqlUpdate("update.topic.count");
+   * 
+   *   update.setParameter("count", 1);
+   *   update.setParameter("topicId", 50);
+   * 
+   *   int modifiedCount = update.execute();
+   *
+   * }
+ */ + public static SqlUpdate createNamedSqlUpdate(String namedQuery) { + return serverMgr.getPrimaryServer().createNamedSqlUpdate(namedQuery); + } + + /** + * Return a named Query that will have defined fetch paths, predicates etc. + *

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

+ * + *
{@code
+   *
+   *   // example
+   *   Query query = Ebean.createNamedQuery(Order.class, "new.for.customer");
+   *   query.setParameter("customerId", 23);
+   *   List newOrders = query.findList();
+   *
+   * }
+ * + * @param beanType + * the class of entity to be fetched + * @param namedQuery + * the name of the query + */ + public static Query createNamedQuery(Class beanType, String namedQuery) { + + return serverMgr.getPrimaryServer().createNamedQuery(beanType, namedQuery); + } + + /** + * Create a query using the query language. + *

+ * Note that you are allowed to add additional clauses using where() as well + * as use fetch() and setOrderBy() after the query has been created. + *

+ *

+ * Note that this method signature used to map to named queries and that has + * moved to {@link #createNamedQuery(Class, String)}. + *

+ * + *
{@code
+   * 
+   *   String q = "find order fetch details where status = :st";
+   * 
+   *   List newOrders = Ebean.>findOrder.class, q)
+   *     .setParameter("st", Order.Status.NEW)
+   *     .findList();
+   *
+   * }
+ * + * @param query + * the object query + */ + public static Query createQuery(Class beanType, String query) { + return serverMgr.getPrimaryServer().createQuery(beanType, query); + } + + /** + * 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 = Ebean.createNamedUpdate(Topic.class, "setPostCount");
+   *   update.setParameter("postCount", 10);
+   *   update.setParameter("id", 3);
+   * 
+   *   int rows = update.execute();
+   *   System.out.println("rows updated: " + rows);
+   *
+   * }
+ */ + public static Update createNamedUpdate(Class beanType, String namedUpdate) { + + return serverMgr.getPrimaryServer().createNamedUpdate(beanType, namedUpdate); + } + + /** + * 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 = Ebean.createUpdate(Topic.class, updStatement);
+   * 
+   *   update.set("pc", 9);
+   *   update.set("id", 3);
+   * 
+   *   int rows = update.execute();
+   *   System.out.println("rows updated:" + rows);
+   *
+   * }
+ */ + public static Update createUpdate(Class beanType, String ormUpdate) { + + return serverMgr.getPrimaryServer().createUpdate(beanType, ormUpdate); + } + + /** + * Create a CsvReader for a given beanType. + */ + public static CsvReader createCsvReader(Class beanType) { + + return serverMgr.getPrimaryServer().createCsvReader(beanType); + } + + /** + * 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 additionally fetching the customer, details and details.product
+   *   // name.
+   * 
+   *   Order order = Ebean.find(Order.class)
+   *     .fetch("customer")
+   *     .fetch("details")
+   *     .fetch("detail.product", "name")
+   *     .setId(2)
+   *     .findUnique();
+   * 
+   *   // Find order 2 additionally fetching the customer, details and details.product
+   *   // name.
+   *   // Note: same query as above but using the query language
+   *   // Note: using a named query would be preferred practice
+   * 
+   *   String oql = "find order fetch customer fetch details fetch details.product (name) where id = :orderId ";
+   * 
+   *   Query query = Ebean.find(Order.class);
+   *   query.setQuery(oql);
+   *   query.setParameter("orderId", 2);
+   * 
+   *   Order order = query.findUnique();
+   * 
+   *   // Using a named query
+   *   Query query = Ebean.find(Order.class, "with.details");
+   *   query.setParameter("orderId", 2);
+   * 
+   *   Order order = query.findUnique();
+   * 
+   * }
+ * + * @param beanType + * the class of entity to be fetched + * @return A ORM Query object for this beanType + */ + public static Query createQuery(Class beanType) { + + return serverMgr.getPrimaryServer().createQuery(beanType); + } + + /** + * Create a query for a type of entity bean. + *

+ * This is actually the same as {@link #createQuery(Class)}. The reason it + * exists is that people used to JPA will probably be looking for a + * createQuery method (the same as entityManager). + *

+ * + * @param beanType + * the type of entity bean to find + * @return A ORM Query object for this beanType + */ + public static Query find(Class beanType) { + + return serverMgr.getPrimaryServer().find(beanType); + } + + /** + * 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 static Filter filter(Class beanType) { + return serverMgr.getPrimaryServer().filter(beanType); + } + + /** + * 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(). + *

+ * If you wish to execute a Sql Select natively then you should use the + * FindByNativeSql object. + *

+ *

+ * 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 = Ebean.createSqlUpdate(s);
+   * 
+   *   update.setParameter("id", 1);
+   *   update.setParameter("count", 50);
+   * 
+   *   int modifiedCount = Ebean.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 SqlUpdate + * @see CallableSql + * @see Ebean#execute(CallableSql) + */ + public static int execute(SqlUpdate sqlUpdate) { + return serverMgr.getPrimaryServer().execute(sqlUpdate); + } + + /** + * For making calls to stored procedures. + *

+ * Example: + *

+ * + *
{@code
+   *
+   *   String sql = "{call sp_order_modify(?,?,?)}";
+   * 
+   *   CallableSql cs = Ebean.createCallableSql(sql);
+   *   cs.setParameter(1, 27);
+   *   cs.setParameter(2, "SHIPPED");
+   *   cs.registerOut(3, Types.INTEGER);
+   * 
+   *   Ebean.execute(cs);
+   * 
+   *   // read the out parameter
+   *   Integer returnValue = (Integer) cs.getObject(3);
+   *
+   * }
+ * + * @see CallableSql + * @see Ebean#execute(SqlUpdate) + */ + public static int execute(CallableSql callableSql) { + return serverMgr.getPrimaryServer().execute(callableSql); + } + + /** + * Execute a TxRunnable in a Transaction with an explicit scope. + *

+ * The scope can control the transaction type, isolation and rollback + * semantics. + *

+ * + *
{@code
+   *
+   *   // set specific transactional scope settings
+   *   TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
+   *
+   *   Ebean.execute(scope, new TxRunnable() {
+   * 	   public void run() {
+   * 		   User u1 = Ebean.find(User.class, 1);
+   * 		   ...
+   * 	   }
+   *   });
+   *
+   * }
+ */ + public static void execute(TxScope scope, TxRunnable r) { + serverMgr.getPrimaryServer().execute(scope, r); + } + + /** + * Execute a TxRunnable in a Transaction with the default scope. + *

+ * The default scope runs with REQUIRED and by default will rollback on any + * exception (checked or runtime). + *

+ * + *
{@code
+   *
+   *   Ebean.execute(new TxRunnable() {
+   *     public void run() {
+   *       User u1 = Ebean.find(User.class, 1);
+   *       User u2 = Ebean.find(User.class, 2);
+   * 
+   *       u1.setName("u1 mod");
+   *       u2.setName("u2 mod");
+   * 
+   *       Ebean.save(u1);
+   *       Ebean.save(u2);
+   *     }
+   *   });
+   *
+   * }
+ */ + public static void execute(TxRunnable r) { + serverMgr.getPrimaryServer().execute(r); + } + + /** + * Execute a TxCallable in a Transaction with an explicit scope. + *

+ * The scope can control the transaction type, isolation and rollback + * semantics. + *

+ * + *
{@code
+   *
+   *   // set specific transactional scope settings
+   *   TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
+   *
+   *   Ebean.execute(scope, new TxCallable() {
+   * 	   public String call() {
+   * 		   User u1 = Ebean.find(User.class, 1);
+   * 		   ...
+   * 		   return u1.getEmail();
+   * 	   }
+   *   });
+   *
+   * }
+ * + */ + public static T execute(TxScope scope, TxCallable c) { + return serverMgr.getPrimaryServer().execute(scope, c); + } + + /** + * Execute a TxCallable in a Transaction with the default scope. + *

+ * 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
+   *
+   *   Ebean.execute(new TxCallable() {
+   *     public String call() {
+   *       User u1 = Ebean.find(User.class, 1);
+   *       User u2 = Ebean.find(User.class, 2);
+   * 
+   *       u1.setName("u1 mod");
+   *       u2.setName("u2 mod");
+   * 
+   *       Ebean.save(u1);
+   *       Ebean.save(u2);
+   * 
+   *       return u1.getEmail();
+   *     }
+   *   });
+   *
+   * }
+ */ + public static T execute(TxCallable c) { + return serverMgr.getPrimaryServer().execute(c); + } + + /** + * 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. + *

+ * If you use Ebean.execute(UpdateSql) then the table modification information + * is automatically deduced and you do not need to call this method yourself. + *

+ *

+ * 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 inserts + * true if rows where inserted into the table + * @param updates + * true if rows on the table where updated + * @param deletes + * true if rows on the table where deleted + */ + public static void externalModification(String tableName, boolean inserts, boolean updates, + boolean deletes) { + + serverMgr.getPrimaryServer().externalModification(tableName, inserts, updates, deletes); + } + + /** + * Return the BeanState for a given entity bean. + *

+ * This will return null if the bean is not an enhanced entity bean. + *

+ */ + public static BeanState getBeanState(Object bean) { + return serverMgr.getPrimaryServer().getBeanState(bean); + } + + /** + * Return the manager of the server cache ("L2" cache). + * + */ + public static ServerCacheManager getServerCacheManager() { + return serverMgr.getPrimaryServer().getServerCacheManager(); + } + + /** + * Return the BackgroundExecutor service for asynchronous processing of + * queries. + */ + public static BackgroundExecutor getBackgroundExecutor() { + return serverMgr.getPrimaryServer().getBackgroundExecutor(); + } + + /** + * Run the cache warming queries on all bean types that have one defined for + * the default/primary EbeanServer. + *

+ * A cache warming query can be defined via {@link CacheStrategy}. + *

+ */ + public static void runCacheWarming() { + serverMgr.getPrimaryServer().runCacheWarming(); + } + + /** + * Run the cache warming query for a specific bean type for the + * default/primary EbeanServer. + *

+ * A cache warming query can be defined via {@link CacheStrategy}. + *

+ */ + public static void runCacheWarming(Class beanType) { + + serverMgr.getPrimaryServer().runCacheWarming(beanType); + } + + /** + * Return the JsonContext for reading/writing JSON. + */ + public static JsonContext json() { + return serverMgr.getPrimaryServer().json(); + } + + /** + * Return the JsonContext for reading/writing JSON. + * @deprecated Please use #json instead. + */ + public static JsonContext createJsonContext() { + return json(); + } + +} diff --git a/src/main/java/com/avaje/ebean/EbeanServer.java b/src/main/java/com/avaje/ebean/EbeanServer.java index 32712f8b4..ba0cbd8b5 100644 --- a/src/main/java/com/avaje/ebean/EbeanServer.java +++ b/src/main/java/com/avaje/ebean/EbeanServer.java @@ -103,30 +103,30 @@ public interface EbeanServer { * if true then deregister the JDBC driver if it is the EbeanORM * DataSource implementation. */ - public void shutdown(boolean shutdownDataSource, boolean deregisterDriver); + void shutdown(boolean shutdownDataSource, boolean deregisterDriver); /** * Return the AdminAutofetch which is used to control and configure the * Autofetch service at runtime. */ - public AdminAutofetch getAdminAutofetch(); + AdminAutofetch getAdminAutofetch(); /** * Return the name. This is used with {@link Ebean#getServer(String)} to get a * EbeanServer that was registered with the Ebean singleton. */ - public String getName(); + String getName(); /** * Return the ExpressionFactory for this server. */ - public ExpressionFactory getExpressionFactory(); + ExpressionFactory getExpressionFactory(); /** * Return the MetaInfoManager which is used to get meta data from the EbeanServer * such as query execution statistics. */ - public MetaInfoManager getMetaInfoManager(); + MetaInfoManager getMetaInfoManager(); /** * Return the BeanState for a given entity bean. @@ -134,12 +134,12 @@ public interface EbeanServer { * This will return null if the bean is not an enhanced entity bean. *

*/ - public BeanState getBeanState(Object bean); + BeanState getBeanState(Object bean); /** * Return the value of the Id property for a given bean. */ - public Object getBeanId(Object bean); + Object getBeanId(Object bean); /** * Return a map of the differences between two objects of the same type. @@ -148,7 +148,7 @@ public interface EbeanServer { * difference comparison. *

*/ - public Map diff(Object a, Object b); + Map diff(Object a, Object b); /** * Create a new instance of T that is an EntityBean. @@ -157,12 +157,12 @@ public interface EbeanServer { * favour of always using enhancement). *

*/ - public T createEntityBean(Class type); + T createEntityBean(Class type); /** * Create a CsvReader for a given beanType. */ - public CsvReader createCsvReader(Class beanType); + CsvReader createCsvReader(Class beanType); /** * Return a named Query that will have defined fetch paths, predicates etc. @@ -182,7 +182,7 @@ public interface EbeanServer { * * } */ - public Query createNamedQuery(Class beanType, String namedQuery); + Query createNamedQuery(Class beanType, String namedQuery); /** * Create a query using the query language. @@ -208,14 +208,14 @@ public interface EbeanServer { * @param query * the object query */ - public Query createQuery(Class beanType, String query); + Query createQuery(Class beanType, String query); /** * Create a query for an entity bean and synonym for {@link #find(Class)}. * * @see #find(Class) */ - public Query createQuery(Class beanType); + Query createQuery(Class beanType); /** * Create a query for a type of entity bean. @@ -257,7 +257,7 @@ public interface EbeanServer { * } * */ - public Query find(Class beanType); + Query find(Class beanType); /** * Return the next unique identity value for a given bean type. @@ -271,7 +271,7 @@ public interface EbeanServer { * complex cases where it is useful to get an ID prior to some processing. *

*/ - public Object nextId(Class beanType); + Object nextId(Class beanType); /** * Create a filter for sorting and filtering lists of entities locally without @@ -283,7 +283,7 @@ public interface EbeanServer { * Refer to {@link Filter} for an example of its use. *

*/ - public Filter filter(Class beanType); + Filter filter(Class beanType); /** * Sort the list in memory using the sortByClause which can contain a comma delimited @@ -327,7 +327,7 @@ public interface EbeanServer { * @param sortByClause * the properties to sort the list by */ - public void sort(List list, String sortByClause); + void sort(List list, String sortByClause); /** * Create a named orm update. The update statement is specified via the @@ -383,7 +383,7 @@ public interface EbeanServer { * * } */ - public Update createNamedUpdate(Class beanType, String namedUpdate); + Update createNamedUpdate(Class beanType, String namedUpdate); /** * Create a orm update where you will supply the insert/update or delete @@ -414,7 +414,7 @@ public interface EbeanServer { * * } */ - public Update createUpdate(Class beanType, String ormUpdate); + Update createUpdate(Class beanType, String ormUpdate); /** * Create a SqlQuery for executing native sql @@ -424,7 +424,7 @@ public interface EbeanServer { * annotation for examples. *

*/ - public SqlQuery createSqlQuery(String sql); + SqlQuery createSqlQuery(String sql); /** * Create a named sql query. @@ -435,7 +435,7 @@ public interface EbeanServer { * @param namedQuery * the name of the query */ - public SqlQuery createNamedSqlQuery(String namedQuery); + SqlQuery createNamedSqlQuery(String namedQuery); /** * Create a sql update for executing native dml statements. @@ -451,12 +451,12 @@ public interface EbeanServer { * xml file (named update) and use {@link #createNamedSqlUpdate(String)} . *

*/ - public SqlUpdate createSqlUpdate(String sql); + SqlUpdate createSqlUpdate(String sql); /** * Create a CallableSql to execute a given stored procedure. */ - public CallableSql createCallableSql(String callableSql); + CallableSql createCallableSql(String callableSql); /** * Create a named sql update. @@ -477,7 +477,7 @@ public interface EbeanServer { * * } */ - public SqlUpdate createNamedSqlUpdate(String namedQuery); + SqlUpdate createNamedSqlUpdate(String namedQuery); /** * Register a TransactionCallback on the currently active transaction. @@ -488,7 +488,7 @@ public interface EbeanServer { * * @throws PersistenceException If there is no currently active transaction */ - public void register(TransactionCallback transactionCallback) throws PersistenceException; + void register(TransactionCallback transactionCallback) throws PersistenceException; /** * Create a new transaction that is not held in TransactionThreadLocal. @@ -498,7 +498,7 @@ public interface EbeanServer { * management. *

*/ - public Transaction createTransaction(); + Transaction createTransaction(); /** * Create a new transaction additionally specifying the isolation level. @@ -506,7 +506,7 @@ public interface EbeanServer { * Note that this transaction is NOT stored in a thread local. *

*/ - public Transaction createTransaction(TxIsolation isolation); + Transaction createTransaction(TxIsolation isolation); /** * Start a new explicit transaction putting it into a ThreadLocal. @@ -579,12 +579,12 @@ public interface EbeanServer { * EbeanServer yourself. *

*/ - public Transaction beginTransaction(); + Transaction beginTransaction(); /** * Start a transaction additionally specifying the isolation level. */ - public Transaction beginTransaction(TxIsolation isolation); + Transaction beginTransaction(TxIsolation isolation); /** * Start a transaction typically specifying REQUIRES_NEW or REQUIRED semantics. @@ -637,22 +637,22 @@ public interface EbeanServer { * * } */ - public Transaction beginTransaction(TxScope scope); + Transaction beginTransaction(TxScope scope); /** * Returns the current transaction or null if there is no current transaction in scope. */ - public Transaction currentTransaction(); + Transaction currentTransaction(); /** * Commit the current transaction. */ - public void commitTransaction(); + void commitTransaction(); /** * Rollback the current transaction. */ - public void rollbackTransaction(); + void rollbackTransaction(); /** * If the current transaction has already been committed do nothing otherwise @@ -683,7 +683,7 @@ public interface EbeanServer { *

* */ - public void endTransaction(); + void endTransaction(); /** * Refresh the values of a bean. @@ -692,7 +692,7 @@ public interface EbeanServer { * are accessed a lazy load will refresh the many property. *

*/ - public void refresh(Object bean); + void refresh(Object bean); /** * Refresh a many property of an entity bean. @@ -703,7 +703,7 @@ public interface EbeanServer { * the 'many' property to be refreshed * */ - public void refreshMany(Object bean, String propertyName); + void refreshMany(Object bean, String propertyName); /** * Find a bean using its unique id. @@ -753,7 +753,7 @@ public interface EbeanServer { * @param id * the id value */ - public T find(Class beanType, Object id); + T find(Class beanType, Object id); /** * Get a reference object. @@ -798,7 +798,7 @@ public interface EbeanServer { * @param id * the id value */ - public T getReference(Class beanType, Object id); + T getReference(Class beanType, Object id); /** * Return the number of 'top level' or 'root' entities this query should @@ -807,14 +807,14 @@ public interface EbeanServer { * @see Query#findRowCount() * @see com.avaje.ebean.Query#findFutureRowCount() */ - public int findRowCount(Query query, Transaction transaction); + 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); + List findIds(Query query, Transaction transaction); /** * Return a QueryIterator for the query. @@ -832,7 +832,7 @@ public interface EbeanServer { * @see Query#findEach(QueryEachConsumer) * @see Query#findEachWhile(QueryEachWhileConsumer) */ - public QueryIterator findIterate(Query query, Transaction transaction); + QueryIterator findIterate(Query query, Transaction transaction); /** * Execute the query visiting the each bean one at a time. @@ -862,7 +862,7 @@ public interface EbeanServer { * @see Query#findEach(QueryEachConsumer) * @see Query#findEachWhile(QueryEachWhileConsumer) */ - public void findEach(Query query, QueryEachConsumer consumer, Transaction transaction); + void findEach(Query query, QueryEachConsumer consumer, Transaction transaction); /** * Execute the query visiting the each bean one at a time. @@ -899,7 +899,7 @@ public interface EbeanServer { * @see Query#findEach(QueryEachConsumer) * @see Query#findEachWhile(QueryEachWhileConsumer) */ - public void findEachWhile(Query query, QueryEachWhileConsumer consumer, Transaction transaction); + void findEachWhile(Query query, QueryEachWhileConsumer consumer, Transaction transaction); /** * Deprecated in favor of #findEachWhile which is functionally exactly the same @@ -912,7 +912,7 @@ public interface EbeanServer { * * @deprecated */ - public void findVisit(Query query, QueryResultVisitor visitor, Transaction transaction); + void findVisit(Query query, QueryResultVisitor visitor, Transaction transaction); /** * Execute a query returning a list of beans. @@ -941,7 +941,7 @@ public interface EbeanServer { * * @see Query#findList() */ - public List findList(Query query, Transaction transaction); + List findList(Query query, Transaction transaction); /** * Execute find row count query in a background thread. @@ -959,7 +959,7 @@ public interface EbeanServer { * * @see com.avaje.ebean.Query#findFutureRowCount() */ - public FutureRowCount findFutureRowCount(Query query, Transaction transaction); + FutureRowCount findFutureRowCount(Query query, Transaction transaction); /** * Execute find Id's query in a background thread. @@ -977,7 +977,7 @@ public interface EbeanServer { * * @see com.avaje.ebean.Query#findFutureIds() */ - public FutureIds findFutureIds(Query query, Transaction transaction); + FutureIds findFutureIds(Query query, Transaction transaction); /** * Execute find list query in a background thread returning a FutureList object. @@ -997,7 +997,7 @@ public interface EbeanServer { * * @see Query#findFutureList() */ - public FutureList findFutureList(Query query, Transaction transaction); + FutureList findFutureList(Query query, Transaction transaction); /** * Execute find list SQL query in a background thread. @@ -1013,7 +1013,7 @@ public interface EbeanServer { * the transaction (can be null). * @return a Future object for the list result of the query */ - public SqlFutureList findFutureList(SqlQuery query, Transaction transaction); + SqlFutureList findFutureList(SqlQuery query, Transaction transaction); /** * Return a PagedList for this query. @@ -1036,7 +1036,7 @@ public interface EbeanServer { * * @see Query#findPagedList(int, int) */ - public PagedList findPagedList(Query query, Transaction transaction, int pageIndex, int pageSize); + PagedList findPagedList(Query query, Transaction transaction, int pageIndex, int pageSize); /** * Execute the query returning a set of entity beans. @@ -1065,7 +1065,7 @@ public interface EbeanServer { * * @see Query#findSet() */ - public Set findSet(Query query, Transaction transaction); + Set findSet(Query query, Transaction transaction); /** * Execute the query returning the entity beans in a Map. @@ -1085,7 +1085,7 @@ public interface EbeanServer { * * @see Query#findMap() */ - public Map findMap(Query query, Transaction transaction); + Map findMap(Query query, Transaction transaction); /** * Execute the query returning at most one entity bean. This will throw a @@ -1106,7 +1106,7 @@ public interface EbeanServer { * * @see Query#findUnique() */ - public T findUnique(Query query, Transaction transaction); + T findUnique(Query query, Transaction transaction); /** * Execute the sql query returning a list of MapBean. @@ -1124,7 +1124,7 @@ public interface EbeanServer { * * @see SqlQuery#findList() */ - public List findList(SqlQuery query, Transaction transaction); + List findList(SqlQuery query, Transaction transaction); /** * Execute the sql query returning a set of MapBean. @@ -1142,7 +1142,7 @@ public interface EbeanServer { * * @see SqlQuery#findSet() */ - public Set findSet(SqlQuery query, Transaction transaction); + Set findSet(SqlQuery query, Transaction transaction); /** * Execute the sql query returning a map of MapBean. @@ -1160,7 +1160,7 @@ public interface EbeanServer { * * @see SqlQuery#findMap() */ - public Map findMap(SqlQuery query, Transaction transaction); + Map findMap(SqlQuery query, Transaction transaction); /** * Execute the sql query returning a single MapBean or null. @@ -1182,7 +1182,7 @@ public interface EbeanServer { * * @see SqlQuery#findUnique() */ - public SqlRow findUnique(SqlQuery query, Transaction transaction); + SqlRow findUnique(SqlQuery query, Transaction transaction); /** * Either Insert or Update the bean depending on its state. @@ -1217,17 +1217,17 @@ public interface EbeanServer { * will be set against each order detail when it is saved. *

*/ - public void save(Object bean) throws OptimisticLockException; + void save(Object bean) throws OptimisticLockException; /** * Save all the beans in the iterator. */ - public int save(Iterator it) throws OptimisticLockException; + int save(Iterator it) throws OptimisticLockException; /** * Save all the beans in the collection. */ - public int save(Collection beans) throws OptimisticLockException; + int save(Collection beans) throws OptimisticLockException; /** * Delete the bean. @@ -1236,38 +1236,38 @@ public interface EbeanServer { * you automatically. *

*/ - public void delete(Object bean) throws OptimisticLockException; + void delete(Object bean) throws OptimisticLockException; /** * Delete all the beans from an Iterator. */ - public int delete(Iterator it) throws OptimisticLockException; + int delete(Iterator it) throws OptimisticLockException; /** * Delete all the beans in the collection. */ - public int delete(Collection c) throws OptimisticLockException; + int delete(Collection c) throws OptimisticLockException; /** * Delete the bean given its type and id. */ - public int delete(Class beanType, Object id); + int delete(Class beanType, Object id); /** * Delete the bean given its type and id with an explicit transaction. */ - public int delete(Class beanType, Object id, Transaction transaction); + int delete(Class beanType, Object id, Transaction transaction); /** * Delete several beans given their type and id values. */ - public void delete(Class beanType, Collection ids); + void delete(Class beanType, Collection ids); /** * Delete several beans given their type and id values with an explicit * transaction. */ - public void delete(Class beanType, Collection ids, Transaction transaction); + void delete(Class beanType, Collection ids, Transaction transaction); /** * Execute a Sql Update Delete or Insert statement. This returns the number of @@ -1310,7 +1310,7 @@ public interface EbeanServer { * * @see CallableSql */ - public int execute(SqlUpdate sqlUpdate); + int execute(SqlUpdate sqlUpdate); /** * Execute a ORM insert update or delete statement using the current @@ -1319,13 +1319,13 @@ public interface EbeanServer { * This returns the number of rows that where inserted, updated or deleted. *

*/ - public int execute(Update update); + int execute(Update update); /** * Execute a ORM insert update or delete statement with an explicit * transaction. */ - public int execute(Update update, Transaction t); + int execute(Update update, Transaction t); /** * For making calls to stored procedures. @@ -1352,7 +1352,7 @@ public interface EbeanServer { * @see CallableSql * @see Ebean#execute(SqlUpdate) */ - public int execute(CallableSql callableSql); + int execute(CallableSql callableSql); /** * Inform Ebean that tables have been modified externally. These could be the @@ -1388,7 +1388,7 @@ public interface EbeanServer { * @param deleted * true if rows on the table where deleted */ - public void externalModification(String tableName, boolean inserted, boolean updated, boolean deleted); + void externalModification(String tableName, boolean inserted, boolean updated, boolean deleted); /** * Find a entity bean with an explicit transaction. @@ -1402,22 +1402,22 @@ public interface EbeanServer { * @param transaction * the transaction to use (can be null) */ - public T find(Class beanType, Object uid, Transaction transaction); + T find(Class beanType, Object uid, Transaction transaction); /** * Insert or update a bean with an explicit transaction. */ - public void save(Object bean, Transaction transaction) throws OptimisticLockException; + void save(Object bean, Transaction transaction) throws OptimisticLockException; /** * Save all the beans in the iterator with an explicit transaction. */ - public int save(Iterator it, Transaction transaction) throws OptimisticLockException; + int save(Iterator it, Transaction transaction) throws OptimisticLockException; /** * Save all the beans in the collection with an explicit transaction. */ - public int save(Collection beans, Transaction transaction) throws OptimisticLockException; + int save(Collection beans, Transaction transaction) throws OptimisticLockException; /** * Marks the entity bean as dirty. @@ -1439,7 +1439,7 @@ public interface EbeanServer { * * } */ - public void markAsDirty(Object bean); + void markAsDirty(Object bean); /** * Saves the bean using an update. If you know you are updating a bean then it is preferrable to @@ -1477,12 +1477,12 @@ public interface EbeanServer { * @see ServerConfig#setUpdatesDeleteMissingChildren(boolean) * @see ServerConfig#setUpdateChangesOnly(boolean) */ - public void update(Object bean) throws OptimisticLockException; + void update(Object bean) throws OptimisticLockException; /** * Update a bean additionally specifying a transaction. */ - public void update(Object bean, Transaction t) throws OptimisticLockException; + void update(Object bean, Transaction t) throws OptimisticLockException; /** * Update a bean additionally specifying a transaction and the deleteMissingChildren setting. @@ -1496,18 +1496,18 @@ public interface EbeanServer { * or ManyToMany to be automatically deleted. */ - public void update(Object bean, Transaction transaction, boolean deleteMissingChildren) throws OptimisticLockException; + void update(Object bean, Transaction transaction, boolean deleteMissingChildren) throws OptimisticLockException; /** * Update a collection of beans. If there is no current transaction one is created and used to * update all the beans in the collection. */ - public void update(Collection beans) throws OptimisticLockException; + void update(Collection beans) throws OptimisticLockException; /** * Update a collection of beans with an explicit transaction. */ - public void update(Collection beans, Transaction transaction) throws OptimisticLockException; + void update(Collection beans, Transaction transaction) throws OptimisticLockException; /** * Insert the bean. @@ -1517,23 +1517,23 @@ public interface EbeanServer { * and want to insert them into another database (and you want to explicitly insert them). *

*/ - public void insert(Object bean); + void insert(Object bean); /** * Insert the bean with a transaction. */ - public void insert(Object bean, Transaction t); + void insert(Object bean, Transaction t); /** * Insert a collection of beans. If there is no current transaction one is created and used to * insert all the beans in the collection. */ - public void insert(Collection beans); + void insert(Collection beans); /** * Insert a collection of beans with an explicit transaction. */ - public void insert(Collection beans, Transaction t); + void insert(Collection beans, Transaction t); /** * Delete the associations (from the intersection table) of a ManyToMany given @@ -1545,7 +1545,7 @@ public interface EbeanServer { * * @return the number of associations deleted (from the intersection table). */ - public int deleteManyToManyAssociations(Object ownerBean, String propertyName); + int deleteManyToManyAssociations(Object ownerBean, String propertyName); /** * Delete the associations (from the intersection table) of a ManyToMany given @@ -1560,7 +1560,7 @@ public interface EbeanServer { * * @return the number of associations deleted (from the intersection table). */ - public int deleteManyToManyAssociations(Object ownerBean, String propertyName, Transaction t); + int deleteManyToManyAssociations(Object ownerBean, String propertyName, Transaction t); /** * Save the associations of a ManyToMany given the owner bean and the @@ -1571,7 +1571,7 @@ public interface EbeanServer { * way to invoke those insertions directly. *

*/ - public void saveManyToManyAssociations(Object ownerBean, String propertyName); + void saveManyToManyAssociations(Object ownerBean, String propertyName); /** * Save the associations of a ManyToMany given the owner bean and the @@ -1582,7 +1582,7 @@ public interface EbeanServer { * way to invoke those insertions directly. *

*/ - public void saveManyToManyAssociations(Object ownerBean, String propertyName, Transaction t); + void saveManyToManyAssociations(Object ownerBean, String propertyName, Transaction t); /** * Save the associated collection or bean given the property name. @@ -1600,7 +1600,7 @@ public interface EbeanServer { * @param propertyName * the property we want to save */ - public void saveAssociation(Object ownerBean, String propertyName); + void saveAssociation(Object ownerBean, String propertyName); /** * Save the associated collection or bean given the property name with a @@ -1619,27 +1619,27 @@ public interface EbeanServer { * @param propertyName * the property we want to save */ - public void saveAssociation(Object ownerBean, String propertyName, Transaction t); + void saveAssociation(Object ownerBean, String propertyName, Transaction t); /** * Delete the bean with an explicit transaction. */ - public void delete(Object bean, Transaction t) throws OptimisticLockException; + void delete(Object bean, Transaction t) throws OptimisticLockException; /** * Delete all the beans from an iterator. */ - public int delete(Iterator it, Transaction t) throws OptimisticLockException; + int delete(Iterator it, Transaction t) throws OptimisticLockException; /** * Execute explicitly passing a transaction. */ - public int execute(SqlUpdate updSql, Transaction t); + int execute(SqlUpdate updSql, Transaction t); /** * Execute explicitly passing a transaction. */ - public int execute(CallableSql callableSql, Transaction t); + int execute(CallableSql callableSql, Transaction t); /** * Execute a TxRunnable in a Transaction with an explicit scope. @@ -1662,7 +1662,7 @@ public interface EbeanServer { * * } */ - public void execute(TxScope scope, TxRunnable r); + void execute(TxScope scope, TxRunnable r); /** * Execute a TxRunnable in a Transaction with the default scope. @@ -1688,7 +1688,7 @@ public interface EbeanServer { * * } */ - public void execute(TxRunnable r); + void execute(TxRunnable r); /** * Execute a TxCallable in a Transaction with an explicit scope. @@ -1712,7 +1712,7 @@ public interface EbeanServer { * * } */ - public T execute(TxScope scope, TxCallable c); + T execute(TxScope scope, TxCallable c); /** * Execute a TxCallable in a Transaction with the default scope. @@ -1744,19 +1744,19 @@ public interface EbeanServer { * * } */ - public T execute(TxCallable c); + T execute(TxCallable c); /** * Return the manager of the server cache ("L2" cache). * */ - public ServerCacheManager getServerCacheManager(); + ServerCacheManager getServerCacheManager(); /** * Return the BackgroundExecutor service for asynchronous processing of * queries. */ - public BackgroundExecutor getBackgroundExecutor(); + BackgroundExecutor getBackgroundExecutor(); /** * Run the cache warming queries on all bean types that have one defined. @@ -1764,7 +1764,7 @@ public interface EbeanServer { * A cache warming query can be defined via {@link CacheStrategy}. *

*/ - public void runCacheWarming(); + void runCacheWarming(); /** * Run the cache warming query for a specific bean type. @@ -1772,13 +1772,13 @@ public interface EbeanServer { * A cache warming query can be defined via {@link CacheStrategy}. *

*/ - public void runCacheWarming(Class beanType); + void runCacheWarming(Class beanType); /** * Return the JsonContext for reading/writing JSON. * @deprecated Please use #json instead. */ - public JsonContext createJsonContext(); + JsonContext createJsonContext(); /** * Return the JsonContext for reading/writing JSON. @@ -1818,6 +1818,6 @@ public interface EbeanServer { * @see com.avaje.ebean.text.PathProperties * @see Query#apply(com.avaje.ebean.text.PathProperties) */ - public JsonContext json(); + JsonContext json(); } diff --git a/src/main/java/com/avaje/ebean/EbeanServerFactory.java b/src/main/java/com/avaje/ebean/EbeanServerFactory.java index 392280d48..4dfa49bcb 100644 --- a/src/main/java/com/avaje/ebean/EbeanServerFactory.java +++ b/src/main/java/com/avaje/ebean/EbeanServerFactory.java @@ -1,121 +1,121 @@ -package com.avaje.ebean; - -import com.avaje.ebean.common.SpiContainer; -import com.avaje.ebean.config.ContainerConfig; -import com.avaje.ebean.config.ServerConfig; - -import javax.persistence.PersistenceException; -import java.lang.reflect.Constructor; -import java.util.Properties; - -/** - * Creates EbeanServer instances. - *

- * This uses either a ServerConfig or properties in the ebean.properties file to - * configure and create a EbeanServer instance. - *

- *

- * The EbeanServer instance can either be registered with the Ebean singleton or - * not. The Ebean singleton effectively holds a map of EbeanServers by a name. - * If the EbeanServer is registered with the Ebean singleton you can retrieve it - * later via {@link Ebean#getServer(String)}. - *

- *

- * One EbeanServer can be nominated as the 'default/primary' EbeanServer. Many - * methods on the Ebean singleton such as {@link Ebean#find(Class)} are just a - * convenient way of using the 'default/primary' EbeanServer. - *

- */ -public class EbeanServerFactory { - - - private static final String DEFAULT_CONTAINER = "com.avaje.ebeaninternal.server.core.DefaultContainer"; - - private static SpiContainer container; - - /** - * Initialise the container with clustering configuration. - * - * Call this prior to creating any EbeanServer instances or alternatively set the - * ContainerConfig on the ServerConfig when creating the first EbeanServer instance. - */ - public static synchronized void initialiseContainer(ContainerConfig containerConfig) { - getContainer(containerConfig); - } - - /** - * Create using ebean.properties to configure the server. - */ - public static synchronized EbeanServer create(String name) { - - // construct based on loading properties files - // and if invoked by Ebean then it handles registration - SpiContainer serverFactory = getContainer(null); - return serverFactory.createServer(name); - } - - /** - * Create using the ServerConfig object to configure the server. - */ - public static synchronized EbeanServer create(ServerConfig config) { - - if (config.getName() == null) { - throw new PersistenceException("The name is null (it is required)"); - } - - EbeanServer server = createInternal(config); - - if (config.isDefaultServer()) { - PrimaryServer.setSkip(true); - } - if (config.isRegister()) { - Ebean.register(server, config.isDefaultServer()); - } - - return server; - } - - - private static EbeanServer createInternal(ServerConfig config) { - - return getContainer(config.getContainerConfig()).createServer(config); - } - - /** - * Get the EbeanContainer initialising it if necessary. - * - * @param containerConfig the configuration controlling clustering communication - */ - private static SpiContainer getContainer(ContainerConfig containerConfig) { - - // thread safe in that all calling methods are synchronized - if (container != null) { - return container; - } - - if (containerConfig == null) { - // effectively load configuration from ebean.properties - Properties properties = PrimaryServer.getProperties(); - containerConfig = new ContainerConfig(); - containerConfig.loadFromProperties(properties); - } - container = createContainer(containerConfig); - return container; - } - - /** - * Create the container instance using the configuration. - */ - private static SpiContainer createContainer(ContainerConfig containerConfig) { - - String implClassName = System.getProperty("ebean.container", DEFAULT_CONTAINER); - - try { - Class cls = Class.forName(implClassName); - Constructor constructor = cls.getConstructor(ContainerConfig.class); - return (SpiContainer) constructor.newInstance(containerConfig); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } -} +package com.avaje.ebean; + +import com.avaje.ebean.common.SpiContainer; +import com.avaje.ebean.config.ContainerConfig; +import com.avaje.ebean.config.ServerConfig; + +import javax.persistence.PersistenceException; +import java.lang.reflect.Constructor; +import java.util.Properties; + +/** + * Creates EbeanServer instances. + *

+ * This uses either a ServerConfig or properties in the ebean.properties file to + * configure and create a EbeanServer instance. + *

+ *

+ * The EbeanServer instance can either be registered with the Ebean singleton or + * not. The Ebean singleton effectively holds a map of EbeanServers by a name. + * If the EbeanServer is registered with the Ebean singleton you can retrieve it + * later via {@link Ebean#getServer(String)}. + *

+ *

+ * One EbeanServer can be nominated as the 'default/primary' EbeanServer. Many + * methods on the Ebean singleton such as {@link Ebean#find(Class)} are just a + * convenient way of using the 'default/primary' EbeanServer. + *

+ */ +public class EbeanServerFactory { + + + private static final String DEFAULT_CONTAINER = "com.avaje.ebeaninternal.server.core.DefaultContainer"; + + private static SpiContainer container; + + /** + * Initialise the container with clustering configuration. + * + * Call this prior to creating any EbeanServer instances or alternatively set the + * ContainerConfig on the ServerConfig when creating the first EbeanServer instance. + */ + public static synchronized void initialiseContainer(ContainerConfig containerConfig) { + getContainer(containerConfig); + } + + /** + * Create using ebean.properties to configure the server. + */ + public static synchronized EbeanServer create(String name) { + + // construct based on loading properties files + // and if invoked by Ebean then it handles registration + SpiContainer serverFactory = getContainer(null); + return serverFactory.createServer(name); + } + + /** + * Create using the ServerConfig object to configure the server. + */ + public static synchronized EbeanServer create(ServerConfig config) { + + if (config.getName() == null) { + throw new PersistenceException("The name is null (it is required)"); + } + + EbeanServer server = createInternal(config); + + if (config.isDefaultServer()) { + PrimaryServer.setSkip(true); + } + if (config.isRegister()) { + Ebean.register(server, config.isDefaultServer()); + } + + return server; + } + + + private static EbeanServer createInternal(ServerConfig config) { + + return getContainer(config.getContainerConfig()).createServer(config); + } + + /** + * Get the EbeanContainer initialising it if necessary. + * + * @param containerConfig the configuration controlling clustering communication + */ + private static SpiContainer getContainer(ContainerConfig containerConfig) { + + // thread safe in that all calling methods are synchronized + if (container != null) { + return container; + } + + if (containerConfig == null) { + // effectively load configuration from ebean.properties + Properties properties = PrimaryServer.getProperties(); + containerConfig = new ContainerConfig(); + containerConfig.loadFromProperties(properties); + } + container = createContainer(containerConfig); + return container; + } + + /** + * Create the container instance using the configuration. + */ + private static SpiContainer createContainer(ContainerConfig containerConfig) { + + String implClassName = System.getProperty("ebean.container", DEFAULT_CONTAINER); + + try { + Class cls = Class.forName(implClassName); + Constructor constructor = cls.getConstructor(ContainerConfig.class); + return (SpiContainer) constructor.newInstance(containerConfig); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/src/main/java/com/avaje/ebean/ExampleExpression.java b/src/main/java/com/avaje/ebean/ExampleExpression.java index fa335b980..e8fcf366a 100644 --- a/src/main/java/com/avaje/ebean/ExampleExpression.java +++ b/src/main/java/com/avaje/ebean/ExampleExpression.java @@ -1,93 +1,93 @@ -package com.avaje.ebean; - -/** - * Query by Example expression. - *

- * Pass in an example entity and for each non-null scalar properties an - * expression is added. - *

- *

- * By Default this case sensitive, will ignore numeric zero values and will use - * a Like for string values (you must put in your own wildcards). - *

- *

- * To get control over the options you can create an ExampleExpression and set - * those options such as case insensitive etc. - *

- * - *
- * // create an example bean and set the properties
- * // with the query parameters you want
- * 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 - * - *
- * 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<Customer> list =
- *     Ebean.find(Customer.class)
- *         .where()
- *         .add(qbe)
- *         .findList();
- * 
- * - * @author Rob Bygrave - */ -public interface ExampleExpression extends Expression { - - /** - * By calling this method zero value properties are going to be included in - * the expression. - *

- * By default numeric zero values are excluded as they can result from - * primitive int and long types. - *

- */ - public ExampleExpression includeZeros(); - - /** - * Set case insensitive to true. - */ - public ExampleExpression caseInsensitive(); - - /** - * Use startsWith expression for string properties. - */ - public ExampleExpression useStartsWith(); - - /** - * Use contains expression for string properties. - */ - public ExampleExpression useContains(); - - /** - * Use endsWith expression for string properties. - */ - public ExampleExpression useEndsWith(); - - /** - * Use equal to expression for string properties. - */ - public ExampleExpression useEqualTo(); - +package com.avaje.ebean; + +/** + * Query by Example expression. + *

+ * Pass in an example entity and for each non-null scalar properties an + * expression is added. + *

+ *

+ * By Default this case sensitive, will ignore numeric zero values and will use + * a Like for string values (you must put in your own wildcards). + *

+ *

+ * To get control over the options you can create an ExampleExpression and set + * those options such as case insensitive etc. + *

+ * + *
+ * // create an example bean and set the properties
+ * // with the query parameters you want
+ * 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 + * + *
+ * 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<Customer> list =
+ *     Ebean.find(Customer.class)
+ *         .where()
+ *         .add(qbe)
+ *         .findList();
+ * 
+ * + * @author Rob Bygrave + */ +public interface ExampleExpression extends Expression { + + /** + * By calling this method zero value properties are going to be included in + * the expression. + *

+ * By default numeric zero values are excluded as they can result from + * primitive int and long types. + *

+ */ + ExampleExpression includeZeros(); + + /** + * Set case insensitive to true. + */ + ExampleExpression caseInsensitive(); + + /** + * Use startsWith expression for string properties. + */ + ExampleExpression useStartsWith(); + + /** + * Use contains expression for string properties. + */ + ExampleExpression useContains(); + + /** + * Use endsWith expression for string properties. + */ + ExampleExpression useEndsWith(); + + /** + * Use equal to expression for string properties. + */ + ExampleExpression useEqualTo(); + } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/Expr.java b/src/main/java/com/avaje/ebean/Expr.java index a3f95fd0f..473c2f9f8 100644 --- a/src/main/java/com/avaje/ebean/Expr.java +++ b/src/main/java/com/avaje/ebean/Expr.java @@ -1,322 +1,322 @@ -package com.avaje.ebean; - -import java.util.Collection; -import java.util.Map; - -/** - * Expression factory for creating standard expressions for WHERE and HAVING - * clauses. - *

- * Generally you will only need to use this object for creating OR, JUNCTION or - * CONJUNCTION expressions. To create simple expressions you will most likely - * just use the methods on the ExpressionList object that is returned via - * {@link Query#where()}. - *

- *

- * This provides a convenient way to create expressions for the 'Default' - * server. It is actually a short cut for using the ExpressionFactory of the - * 'default' EbeanServer. - *

- * See also {@link Ebean#getExpressionFactory()} - *

- *

- * Creates standard common expressions for using in a Query Where or Having - * clause. - *

- * - *
- *  // Example: Using an Expr.or() method
- * Query<Order> query = Ebean.createQuery(Order.class);
- * query.where( 
- * 		Expr.or(Expr.eq("status", Order.NEW),
- *     		    Expr.gt("orderDate", lastWeek));
- *     
- * List<Order> list = query.findList();
- * ...
- * 
- * - * @see Query#where() - * @author Rob Bygrave - */ -public class Expr { - - private Expr() { - } - - /** - * Equal To - property equal to the given value. - */ - public static Expression eq(String propertyName, Object value) { - return Ebean.getExpressionFactory().eq(propertyName, value); - } - - /** - * Not Equal To - property not equal to the given value. - */ - public static Expression ne(String propertyName, Object value) { - return Ebean.getExpressionFactory().ne(propertyName, value); - } - - /** - * Case Insensitive Equal To - property equal to the given value (typically - * using a lower() function to make it case insensitive). - */ - public static Expression ieq(String propertyName, String value) { - return Ebean.getExpressionFactory().ieq(propertyName, value); - } - - /** - * Between - property between the two given values. - */ - public static Expression between(String propertyName, Object value1, Object value2) { - - return Ebean.getExpressionFactory().between(propertyName, value1, value2); - } - - /** - * Between - value between two given properties. - */ - public static Expression between(String lowProperty, String highProperty, Object value) { - - return Ebean.getExpressionFactory().betweenProperties(lowProperty, highProperty, value); - } - - /** - * Greater Than - property greater than the given value. - */ - public static Expression gt(String propertyName, Object value) { - return Ebean.getExpressionFactory().gt(propertyName, value); - } - - /** - * Greater Than or Equal to - property greater than or equal to the given - * value. - */ - public static Expression ge(String propertyName, Object value) { - return Ebean.getExpressionFactory().ge(propertyName, value); - } - - /** - * Less Than - property less than the given value. - */ - public static Expression lt(String propertyName, Object value) { - return Ebean.getExpressionFactory().lt(propertyName, value); - } - - /** - * Less Than or Equal to - property less than or equal to the given value. - */ - public static Expression le(String propertyName, Object value) { - return Ebean.getExpressionFactory().le(propertyName, value); - } - - /** - * Is Null - property is null. - */ - public static Expression isNull(String propertyName) { - return Ebean.getExpressionFactory().isNull(propertyName); - } - - /** - * Is Not Null - property is not null. - */ - public static Expression isNotNull(String propertyName) { - return Ebean.getExpressionFactory().isNotNull(propertyName); - } - - /** - * Case insensitive {@link #exampleLike(Object)} - */ - public static ExampleExpression iexampleLike(Object example) { - return Ebean.getExpressionFactory().iexampleLike(example); - } - - /** - * Create the query by Example expression which is case sensitive and using - * LikeType.RAW (you need to add you own wildcards % and _). - */ - public static ExampleExpression exampleLike(Object example) { - return Ebean.getExpressionFactory().exampleLike(example); - } - - /** - * Create the query by Example expression specifying more options. - */ - public static ExampleExpression exampleLike(Object example, boolean caseInsensitive, - LikeType likeType) { - return Ebean.getExpressionFactory().exampleLike(example, caseInsensitive, likeType); - } - - /** - * Like - property like value where the value contains the SQL wild card - * characters % (percentage) and _ (underscore). - */ - public static Expression like(String propertyName, String value) { - return Ebean.getExpressionFactory().like(propertyName, value); - } - - /** - * Case insensitive Like - property like value where the value contains the - * SQL wild card characters % (percentage) and _ (underscore). Typically uses - * a lower() function to make the expression case insensitive. - */ - public static Expression ilike(String propertyName, String value) { - return Ebean.getExpressionFactory().ilike(propertyName, value); - } - - /** - * Starts With - property like value%. - */ - public static Expression startsWith(String propertyName, String value) { - return Ebean.getExpressionFactory().startsWith(propertyName, value); - } - - /** - * Case insensitive Starts With - property like value%. Typically uses a - * lower() function to make the expression case insensitive. - */ - public static Expression istartsWith(String propertyName, String value) { - return Ebean.getExpressionFactory().istartsWith(propertyName, value); - } - - /** - * Ends With - property like %value. - */ - public static Expression endsWith(String propertyName, String value) { - return Ebean.getExpressionFactory().endsWith(propertyName, value); - } - - /** - * Case insensitive Ends With - property like %value. Typically uses a lower() - * function to make the expression case insensitive. - */ - public static Expression iendsWith(String propertyName, String value) { - return Ebean.getExpressionFactory().iendsWith(propertyName, value); - } - - /** - * Contains - property like %value%. - */ - public static Expression contains(String propertyName, String value) { - return Ebean.getExpressionFactory().contains(propertyName, value); - } - - /** - * Case insensitive Contains - property like %value%. Typically uses a lower() - * function to make the expression case insensitive. - */ - public static Expression icontains(String propertyName, String value) { - return Ebean.getExpressionFactory().icontains(propertyName, value); - } - - /** - * In - property has a value in the array of values. - */ - public static Expression in(String propertyName, Object[] values) { - return Ebean.getExpressionFactory().in(propertyName, values); - } - - /** - * In - using a subQuery. - */ - public static Expression in(String propertyName, Query subQuery) { - return Ebean.getExpressionFactory().in(propertyName, subQuery); - } - - /** - * In - property has a value in the collection of values. - */ - public static Expression in(String propertyName, Collection values) { - return Ebean.getExpressionFactory().in(propertyName, values); - } - - /** - * Id Equal to - ID property is equal to the value. - */ - public static Expression idEq(Object value) { - return Ebean.getExpressionFactory().idEq(value); - } - - /** - * All Equal - Map containing property names and their values. - *

- * Expression where all the property names in the map are equal to the - * corresponding value. - *

- * - * @param propertyMap - * a map keyed by property names. - */ - public static Expression allEq(Map propertyMap) { - return Ebean.getExpressionFactory().allEq(propertyMap); - } - - /** - * Add raw expression with a single parameter. - *

- * The raw expression should contain a single ? at the location of the - * parameter. - *

- */ - public static Expression raw(String raw, Object value) { - return Ebean.getExpressionFactory().raw(raw, value); - } - - /** - * Add raw expression with an array of parameters. - *

- * The raw expression should contain the same number of ? as there are - * parameters. - *

- */ - public static Expression raw(String raw, Object[] values) { - return Ebean.getExpressionFactory().raw(raw, values); - } - - /** - * Add raw expression with no parameters. - */ - public static Expression raw(String raw) { - return Ebean.getExpressionFactory().raw(raw); - } - - /** - * And - join two expressions with a logical and. - */ - public static Expression and(Expression expOne, Expression expTwo) { - - return Ebean.getExpressionFactory().and(expOne, expTwo); - } - - /** - * Or - join two expressions with a logical or. - */ - public static Expression or(Expression expOne, Expression expTwo) { - - return Ebean.getExpressionFactory().or(expOne, expTwo); - } - - /** - * Negate the expression (prefix it with NOT). - */ - public static Expression not(Expression exp) { - - return Ebean.getExpressionFactory().not(exp); - } - - /** - * Return a list of expressions that will be joined by AND's. - */ - public static Junction conjunction(Query query) { - - return Ebean.getExpressionFactory().conjunction(query); - } - - /** - * Return a list of expressions that will be joined by OR's. - */ - public static Junction disjunction(Query query) { - - return Ebean.getExpressionFactory().disjunction(query); - } -} +package com.avaje.ebean; + +import java.util.Collection; +import java.util.Map; + +/** + * Expression factory for creating standard expressions for WHERE and HAVING + * clauses. + *

+ * Generally you will only need to use this object for creating OR, JUNCTION or + * CONJUNCTION expressions. To create simple expressions you will most likely + * just use the methods on the ExpressionList object that is returned via + * {@link Query#where()}. + *

+ *

+ * This provides a convenient way to create expressions for the 'Default' + * server. It is actually a short cut for using the ExpressionFactory of the + * 'default' EbeanServer. + *

+ * See also {@link Ebean#getExpressionFactory()} + *

+ *

+ * Creates standard common expressions for using in a Query Where or Having + * clause. + *

+ * + *
+ *  // Example: Using an Expr.or() method
+ * Query<Order> query = Ebean.createQuery(Order.class);
+ * query.where( 
+ * 		Expr.or(Expr.eq("status", Order.NEW),
+ *     		    Expr.gt("orderDate", lastWeek));
+ *     
+ * List<Order> list = query.findList();
+ * ...
+ * 
+ * + * @see Query#where() + * @author Rob Bygrave + */ +public class Expr { + + private Expr() { + } + + /** + * Equal To - property equal to the given value. + */ + public static Expression eq(String propertyName, Object value) { + return Ebean.getExpressionFactory().eq(propertyName, value); + } + + /** + * Not Equal To - property not equal to the given value. + */ + public static Expression ne(String propertyName, Object value) { + return Ebean.getExpressionFactory().ne(propertyName, value); + } + + /** + * Case Insensitive Equal To - property equal to the given value (typically + * using a lower() function to make it case insensitive). + */ + public static Expression ieq(String propertyName, String value) { + return Ebean.getExpressionFactory().ieq(propertyName, value); + } + + /** + * Between - property between the two given values. + */ + public static Expression between(String propertyName, Object value1, Object value2) { + + return Ebean.getExpressionFactory().between(propertyName, value1, value2); + } + + /** + * Between - value between two given properties. + */ + public static Expression between(String lowProperty, String highProperty, Object value) { + + return Ebean.getExpressionFactory().betweenProperties(lowProperty, highProperty, value); + } + + /** + * Greater Than - property greater than the given value. + */ + public static Expression gt(String propertyName, Object value) { + return Ebean.getExpressionFactory().gt(propertyName, value); + } + + /** + * Greater Than or Equal to - property greater than or equal to the given + * value. + */ + public static Expression ge(String propertyName, Object value) { + return Ebean.getExpressionFactory().ge(propertyName, value); + } + + /** + * Less Than - property less than the given value. + */ + public static Expression lt(String propertyName, Object value) { + return Ebean.getExpressionFactory().lt(propertyName, value); + } + + /** + * Less Than or Equal to - property less than or equal to the given value. + */ + public static Expression le(String propertyName, Object value) { + return Ebean.getExpressionFactory().le(propertyName, value); + } + + /** + * Is Null - property is null. + */ + public static Expression isNull(String propertyName) { + return Ebean.getExpressionFactory().isNull(propertyName); + } + + /** + * Is Not Null - property is not null. + */ + public static Expression isNotNull(String propertyName) { + return Ebean.getExpressionFactory().isNotNull(propertyName); + } + + /** + * Case insensitive {@link #exampleLike(Object)} + */ + public static ExampleExpression iexampleLike(Object example) { + return Ebean.getExpressionFactory().iexampleLike(example); + } + + /** + * Create the query by Example expression which is case sensitive and using + * LikeType.RAW (you need to add you own wildcards % and _). + */ + public static ExampleExpression exampleLike(Object example) { + return Ebean.getExpressionFactory().exampleLike(example); + } + + /** + * Create the query by Example expression specifying more options. + */ + public static ExampleExpression exampleLike(Object example, boolean caseInsensitive, + LikeType likeType) { + return Ebean.getExpressionFactory().exampleLike(example, caseInsensitive, likeType); + } + + /** + * Like - property like value where the value contains the SQL wild card + * characters % (percentage) and _ (underscore). + */ + public static Expression like(String propertyName, String value) { + return Ebean.getExpressionFactory().like(propertyName, value); + } + + /** + * Case insensitive Like - property like value where the value contains the + * SQL wild card characters % (percentage) and _ (underscore). Typically uses + * a lower() function to make the expression case insensitive. + */ + public static Expression ilike(String propertyName, String value) { + return Ebean.getExpressionFactory().ilike(propertyName, value); + } + + /** + * Starts With - property like value%. + */ + public static Expression startsWith(String propertyName, String value) { + return Ebean.getExpressionFactory().startsWith(propertyName, value); + } + + /** + * Case insensitive Starts With - property like value%. Typically uses a + * lower() function to make the expression case insensitive. + */ + public static Expression istartsWith(String propertyName, String value) { + return Ebean.getExpressionFactory().istartsWith(propertyName, value); + } + + /** + * Ends With - property like %value. + */ + public static Expression endsWith(String propertyName, String value) { + return Ebean.getExpressionFactory().endsWith(propertyName, value); + } + + /** + * Case insensitive Ends With - property like %value. Typically uses a lower() + * function to make the expression case insensitive. + */ + public static Expression iendsWith(String propertyName, String value) { + return Ebean.getExpressionFactory().iendsWith(propertyName, value); + } + + /** + * Contains - property like %value%. + */ + public static Expression contains(String propertyName, String value) { + return Ebean.getExpressionFactory().contains(propertyName, value); + } + + /** + * Case insensitive Contains - property like %value%. Typically uses a lower() + * function to make the expression case insensitive. + */ + public static Expression icontains(String propertyName, String value) { + return Ebean.getExpressionFactory().icontains(propertyName, value); + } + + /** + * In - property has a value in the array of values. + */ + public static Expression in(String propertyName, Object[] values) { + return Ebean.getExpressionFactory().in(propertyName, values); + } + + /** + * In - using a subQuery. + */ + public static Expression in(String propertyName, Query subQuery) { + return Ebean.getExpressionFactory().in(propertyName, subQuery); + } + + /** + * In - property has a value in the collection of values. + */ + public static Expression in(String propertyName, Collection values) { + return Ebean.getExpressionFactory().in(propertyName, values); + } + + /** + * Id Equal to - ID property is equal to the value. + */ + public static Expression idEq(Object value) { + return Ebean.getExpressionFactory().idEq(value); + } + + /** + * All Equal - Map containing property names and their values. + *

+ * Expression where all the property names in the map are equal to the + * corresponding value. + *

+ * + * @param propertyMap + * a map keyed by property names. + */ + public static Expression allEq(Map propertyMap) { + return Ebean.getExpressionFactory().allEq(propertyMap); + } + + /** + * Add raw expression with a single parameter. + *

+ * The raw expression should contain a single ? at the location of the + * parameter. + *

+ */ + public static Expression raw(String raw, Object value) { + return Ebean.getExpressionFactory().raw(raw, value); + } + + /** + * Add raw expression with an array of parameters. + *

+ * The raw expression should contain the same number of ? as there are + * parameters. + *

+ */ + public static Expression raw(String raw, Object[] values) { + return Ebean.getExpressionFactory().raw(raw, values); + } + + /** + * Add raw expression with no parameters. + */ + public static Expression raw(String raw) { + return Ebean.getExpressionFactory().raw(raw); + } + + /** + * And - join two expressions with a logical and. + */ + public static Expression and(Expression expOne, Expression expTwo) { + + return Ebean.getExpressionFactory().and(expOne, expTwo); + } + + /** + * Or - join two expressions with a logical or. + */ + public static Expression or(Expression expOne, Expression expTwo) { + + return Ebean.getExpressionFactory().or(expOne, expTwo); + } + + /** + * Negate the expression (prefix it with NOT). + */ + public static Expression not(Expression exp) { + + return Ebean.getExpressionFactory().not(exp); + } + + /** + * Return a list of expressions that will be joined by AND's. + */ + public static Junction conjunction(Query query) { + + return Ebean.getExpressionFactory().conjunction(query); + } + + /** + * Return a list of expressions that will be joined by OR's. + */ + public static Junction disjunction(Query query) { + + return Ebean.getExpressionFactory().disjunction(query); + } +} diff --git a/src/main/java/com/avaje/ebean/Expression.java b/src/main/java/com/avaje/ebean/Expression.java index 28308a1d2..c6c38ab2c 100644 --- a/src/main/java/com/avaje/ebean/Expression.java +++ b/src/main/java/com/avaje/ebean/Expression.java @@ -1,10 +1,10 @@ -package com.avaje.ebean; - -import java.io.Serializable; - -/** - * An expression that is part of a WHERE or HAVING clause. - */ -public interface Expression extends Serializable { - -} +package com.avaje.ebean; + +import java.io.Serializable; + +/** + * An expression that is part of a WHERE or HAVING clause. + */ +public interface Expression extends Serializable { + +} diff --git a/src/main/java/com/avaje/ebean/ExpressionFactory.java b/src/main/java/com/avaje/ebean/ExpressionFactory.java index 0479e3ea3..398ef08a9 100644 --- a/src/main/java/com/avaje/ebean/ExpressionFactory.java +++ b/src/main/java/com/avaje/ebean/ExpressionFactory.java @@ -1,262 +1,262 @@ -package com.avaje.ebean; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * Expression factory for creating standard expressions. - *

- * Creates standard common expressions for using in a Query Where or Having - * clause. - *

- *

- * You will often not use this class directly but instead just add expressions - * via the methods on ExpressionList such as - * {@link ExpressionList#gt(String, Object)}. - *

- *

- * The ExpressionList is returned from {@link Query#where()}. - *

- * - *
- *  // Example: fetch orders where status equals new or orderDate > lastWeek.
- *  
- * Expression newOrLastWeek = 
- *   Expr.or(Expr.eq("status", Order.Status.NEW), 
- *           Expr.gt("orderDate", lastWeek));
- * 
- * Query<Order> query = Ebean.createQuery(Order.class);
- * query.where().add(newOrLastWeek);
- * List<Order> list = query.findList();
- * ...
- * 
- * - * @see Query#where() - */ -public interface ExpressionFactory { - - /** - * Equal To - property equal to the given value. - */ - public Expression eq(String propertyName, Object value); - - /** - * Not Equal To - property not equal to the given value. - */ - public Expression ne(String propertyName, Object value); - - /** - * Case Insensitive Equal To - property equal to the given value (typically - * using a lower() function to make it case insensitive). - */ - public Expression ieq(String propertyName, String value); - - /** - * Between - property between the two given values. - */ - public Expression between(String propertyName, Object value1, Object value2); - - /** - * Between - value between two given properties. - */ - public Expression betweenProperties(String lowProperty, String highProperty, Object value); - - /** - * Greater Than - property greater than the given value. - */ - public Expression gt(String propertyName, Object value); - - /** - * Greater Than or Equal to - property greater than or equal to the given - * value. - */ - public Expression ge(String propertyName, Object value); - - /** - * Less Than - property less than the given value. - */ - public Expression lt(String propertyName, Object value); - - /** - * Less Than or Equal to - property less than or equal to the given value. - */ - public Expression le(String propertyName, Object value); - - /** - * Is Null - property is null. - */ - public Expression isNull(String propertyName); - - /** - * Is Not Null - property is not null. - */ - public Expression isNotNull(String propertyName); - - /** - * Case insensitive {@link #exampleLike(Object)} - */ - public ExampleExpression iexampleLike(Object example); - - /** - * Create the query by Example expression which is case sensitive and using - * LikeType.RAW (you need to add you own wildcards % and _). - */ - public ExampleExpression exampleLike(Object example); - - /** - * Create the query by Example expression specifying more options. - */ - public ExampleExpression exampleLike(Object example, boolean caseInsensitive, LikeType likeType); - - /** - * Like - property like value where the value contains the SQL wild card - * characters % (percentage) and _ (underscore). - */ - public Expression like(String propertyName, String value); - - /** - * Case insensitive Like - property like value where the value contains the - * SQL wild card characters % (percentage) and _ (underscore). Typically uses - * a lower() function to make the expression case insensitive. - */ - public Expression ilike(String propertyName, String value); - - /** - * Starts With - property like value%. - */ - public Expression startsWith(String propertyName, String value); - - /** - * Case insensitive Starts With - property like value%. Typically uses a - * lower() function to make the expression case insensitive. - */ - public Expression istartsWith(String propertyName, String value); - - /** - * Ends With - property like %value. - */ - public Expression endsWith(String propertyName, String value); - - /** - * Case insensitive Ends With - property like %value. Typically uses a lower() - * function to make the expression case insensitive. - */ - public Expression iendsWith(String propertyName, String value); - - /** - * Contains - property like %value%. - */ - public Expression contains(String propertyName, String value); - - /** - * Case insensitive Contains - property like %value%. Typically uses a lower() - * function to make the expression case insensitive. - */ - public Expression icontains(String propertyName, String value); - - /** - * In - property has a value in the array of values. - */ - public Expression in(String propertyName, Object[] values); - - /** - * In - using a subQuery. - */ - public Expression in(String propertyName, Query subQuery); - - /** - * In - property has a value in the collection of values. - */ - public Expression in(String propertyName, Collection values); - - /** - * Exists expression - */ - public Expression exists(Query subQuery); - - /** - * Not exists expression - */ - public Expression notExists(Query subQuery); - - /** - * Id Equal to - ID property is equal to the value. - */ - public Expression idEq(Object value); - - /** - * Id IN a list of Id values. - */ - public Expression idIn(List idList); - - /** - * All Equal - Map containing property names and their values. - *

- * Expression where all the property names in the map are equal to the - * corresponding value. - *

- * - * @param propertyMap - * a map keyed by property names. - */ - public Expression allEq(Map propertyMap); - - /** - * Add raw expression with a single parameter. - *

- * The raw expression should contain a single ? at the location of the - * parameter. - *

- */ - public Expression raw(String raw, Object value); - - /** - * Add raw expression with an array of parameters. - *

- * The raw expression should contain the same number of ? as there are - * parameters. - *

- */ - public Expression raw(String raw, Object[] values); - - /** - * Add raw expression with no parameters. - */ - public Expression raw(String raw); - - /** - * And - join two expressions with a logical and. - */ - public Expression and(Expression expOne, Expression expTwo); - - /** - * Or - join two expressions with a logical or. - */ - public Expression or(Expression expOne, Expression expTwo); - - /** - * Negate the expression (prefix it with NOT). - */ - public Expression not(Expression exp); - - /** - * Return a list of expressions that will be joined by AND's. - */ - public Junction conjunction(Query query); - - /** - * Return a list of expressions that will be joined by OR's. - */ - public Junction disjunction(Query query); - - /** - * Return a list of expressions that will be joined by AND's. - */ - public Junction conjunction(Query query, ExpressionList parent); - - /** - * Return a list of expressions that will be joined by OR's. - */ - public Junction disjunction(Query query, ExpressionList parent); -} +package com.avaje.ebean; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Expression factory for creating standard expressions. + *

+ * Creates standard common expressions for using in a Query Where or Having + * clause. + *

+ *

+ * You will often not use this class directly but instead just add expressions + * via the methods on ExpressionList such as + * {@link ExpressionList#gt(String, Object)}. + *

+ *

+ * The ExpressionList is returned from {@link Query#where()}. + *

+ * + *
+ *  // Example: fetch orders where status equals new or orderDate > lastWeek.
+ *  
+ * Expression newOrLastWeek = 
+ *   Expr.or(Expr.eq("status", Order.Status.NEW), 
+ *           Expr.gt("orderDate", lastWeek));
+ * 
+ * Query<Order> query = Ebean.createQuery(Order.class);
+ * query.where().add(newOrLastWeek);
+ * List<Order> list = query.findList();
+ * ...
+ * 
+ * + * @see Query#where() + */ +public interface ExpressionFactory { + + /** + * Equal To - property equal to the given value. + */ + Expression eq(String propertyName, Object value); + + /** + * Not Equal To - property not equal to the given value. + */ + Expression ne(String propertyName, Object value); + + /** + * Case Insensitive Equal To - property equal to the given value (typically + * using a lower() function to make it case insensitive). + */ + Expression ieq(String propertyName, String value); + + /** + * Between - property between the two given values. + */ + Expression between(String propertyName, Object value1, Object value2); + + /** + * Between - value between two given properties. + */ + Expression betweenProperties(String lowProperty, String highProperty, Object value); + + /** + * Greater Than - property greater than the given value. + */ + Expression gt(String propertyName, Object value); + + /** + * Greater Than or Equal to - property greater than or equal to the given + * value. + */ + Expression ge(String propertyName, Object value); + + /** + * Less Than - property less than the given value. + */ + Expression lt(String propertyName, Object value); + + /** + * Less Than or Equal to - property less than or equal to the given value. + */ + Expression le(String propertyName, Object value); + + /** + * Is Null - property is null. + */ + Expression isNull(String propertyName); + + /** + * Is Not Null - property is not null. + */ + Expression isNotNull(String propertyName); + + /** + * Case insensitive {@link #exampleLike(Object)} + */ + ExampleExpression iexampleLike(Object example); + + /** + * Create the query by Example expression which is case sensitive and using + * LikeType.RAW (you need to add you own wildcards % and _). + */ + ExampleExpression exampleLike(Object example); + + /** + * Create the query by Example expression specifying more options. + */ + ExampleExpression exampleLike(Object example, boolean caseInsensitive, LikeType likeType); + + /** + * Like - property like value where the value contains the SQL wild card + * characters % (percentage) and _ (underscore). + */ + Expression like(String propertyName, String value); + + /** + * Case insensitive Like - property like value where the value contains the + * SQL wild card characters % (percentage) and _ (underscore). Typically uses + * a lower() function to make the expression case insensitive. + */ + Expression ilike(String propertyName, String value); + + /** + * Starts With - property like value%. + */ + Expression startsWith(String propertyName, String value); + + /** + * Case insensitive Starts With - property like value%. Typically uses a + * lower() function to make the expression case insensitive. + */ + Expression istartsWith(String propertyName, String value); + + /** + * Ends With - property like %value. + */ + Expression endsWith(String propertyName, String value); + + /** + * Case insensitive Ends With - property like %value. Typically uses a lower() + * function to make the expression case insensitive. + */ + Expression iendsWith(String propertyName, String value); + + /** + * Contains - property like %value%. + */ + Expression contains(String propertyName, String value); + + /** + * Case insensitive Contains - property like %value%. Typically uses a lower() + * function to make the expression case insensitive. + */ + Expression icontains(String propertyName, String value); + + /** + * In - property has a value in the array of values. + */ + Expression in(String propertyName, Object[] values); + + /** + * In - using a subQuery. + */ + Expression in(String propertyName, Query subQuery); + + /** + * In - property has a value in the collection of values. + */ + Expression in(String propertyName, Collection values); + + /** + * Exists expression + */ + Expression exists(Query subQuery); + + /** + * Not exists expression + */ + Expression notExists(Query subQuery); + + /** + * Id Equal to - ID property is equal to the value. + */ + Expression idEq(Object value); + + /** + * Id IN a list of Id values. + */ + Expression idIn(List idList); + + /** + * All Equal - Map containing property names and their values. + *

+ * Expression where all the property names in the map are equal to the + * corresponding value. + *

+ * + * @param propertyMap + * a map keyed by property names. + */ + Expression allEq(Map propertyMap); + + /** + * Add raw expression with a single parameter. + *

+ * The raw expression should contain a single ? at the location of the + * parameter. + *

+ */ + Expression raw(String raw, Object value); + + /** + * Add raw expression with an array of parameters. + *

+ * The raw expression should contain the same number of ? as there are + * parameters. + *

+ */ + Expression raw(String raw, Object[] values); + + /** + * Add raw expression with no parameters. + */ + Expression raw(String raw); + + /** + * And - join two expressions with a logical and. + */ + Expression and(Expression expOne, Expression expTwo); + + /** + * Or - join two expressions with a logical or. + */ + Expression or(Expression expOne, Expression expTwo); + + /** + * Negate the expression (prefix it with NOT). + */ + Expression not(Expression exp); + + /** + * Return a list of expressions that will be joined by AND's. + */ + Junction conjunction(Query query); + + /** + * Return a list of expressions that will be joined by OR's. + */ + Junction disjunction(Query query); + + /** + * Return a list of expressions that will be joined by AND's. + */ + Junction conjunction(Query query, ExpressionList parent); + + /** + * Return a list of expressions that will be joined by OR's. + */ + Junction disjunction(Query query, ExpressionList parent); +} diff --git a/src/main/java/com/avaje/ebean/ExpressionList.java b/src/main/java/com/avaje/ebean/ExpressionList.java index 93bc1692b..b3dcb6754 100644 --- a/src/main/java/com/avaje/ebean/ExpressionList.java +++ b/src/main/java/com/avaje/ebean/ExpressionList.java @@ -1,581 +1,581 @@ -package com.avaje.ebean; - -import com.avaje.ebean.text.PathProperties; - -import java.io.Serializable; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * List of Expressions that make up a where or having clause. - *

- * An ExpressionList is returned from {@link Query#where()}. - *

- *

- * The ExpressionList has a list of convenience methods that create the standard - * expressions and add them to this list. - *

- *

- * The ExpressionList also duplicates methods that are found on the Query such - * as findList() and orderBy(). The purpose of these methods is provide a fluid - * API. The upside of this approach is that you can build and execute a query - * via chained methods. The down side is that this ExpressionList object has - * more methods than you would initially expect (the ones duplicated from - * Query). - *

- * - * @see Query#where() - */ -public interface ExpressionList extends Serializable { - - /** - * Return the query that owns this expression list. - *

- * This is a convenience method solely to support a fluid API where the - * methods are chained together. Adding expressions returns this expression - * list and this method can be used after that to return back the original - * query so that further things can be added to it. - *

- */ - public Query query(); - - /** - * Set the order by clause replacing the existing order by clause if there is - * one. - *

- * This follows SQL syntax using commas between each property with the - * optional asc and desc keywords representing ascending and descending order - * respectively. - *

- *

- * This is EXACTLY the same as {@link #orderBy(String)}. - *

- */ - public Query order(String orderByClause); - - /** - * Return the OrderBy so that you can append an ascending or descending - * property to the order by clause. - *

- * This will never return a null. If no order by clause exists then an 'empty' - * OrderBy object is returned. - *

- */ - public OrderBy order(); - - /** - * Return the OrderBy so that you can append an ascending or descending - * property to the order by clause. - *

- * This will never return a null. If no order by clause exists then an 'empty' - * OrderBy object is returned. - *

- */ - public OrderBy orderBy(); - - /** - * Add an orderBy clause to the query. - * - * @see Query#orderBy(String) - */ - public Query orderBy(String orderBy); - - /** - * Add an orderBy clause to the query. - * - * @see Query#orderBy(String) - */ - public Query setOrderBy(String orderBy); - - /** - * Apply the path properties to the query replacing the select and fetch clauses. - */ - public Query apply(PathProperties pathProperties); - - /** - * Execute the query iterating over the results. - * - * @see Query#findIterate() - */ - public QueryIterator findIterate(); - - /** - * Execute the query process the beans one at a time. - * - * @see Query#findEach(QueryEachConsumer) - */ - public void findEach(QueryEachConsumer consumer); - - /** - * Execute the query processing the beans one at a time with the ability to - * stop processing before reading all the beans. - * - * @see Query#findEachWhile(QueryEachWhileConsumer) - */ - public void findEachWhile(QueryEachWhileConsumer consumer); - - /** - * Deprecated in favor of #findEachWhile which is functionally exactly the same - * but has a much better name. - * - * @deprecated - */ - public void findVisit(QueryResultVisitor visitor); - - /** - * Execute the query returning a list. - * - * @see Query#findList() - */ - public List findList(); - - /** - * Execute the query returning the list of Id's. - * - * @see Query#findIds() - */ - public List findIds(); - - /** - * Return the count of entities this query should return. - *

- * This is the number of 'top level' or 'root level' entities. - *

- */ - public int findRowCount(); - - /** - * Execute the query returning a set. - * - * @see Query#findSet() - */ - public Set findSet(); - - /** - * Execute the query returning a map. - * - * @see Query#findMap() - */ - public Map findMap(); - - /** - * Return a typed map specifying the key property and type. - */ - public Map findMap(String keyProperty, Class keyType); - - /** - * Execute the query returning a single bean. - * - * @see Query#findUnique() - */ - public T findUnique(); - - /** - * 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 - */ - public FutureRowCount findFutureRowCount(); - - /** - * Execute find Id's 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 list of Id's - */ - public FutureIds findFutureIds(); - - /** - * Execute find list 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 list result of the query - */ - public FutureList findFutureList(); - - /** - * Return a PagedList for this query. - *

- * The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and - * {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to - * {@link Query#findFutureRowCount()} to determine total row count, total page count etc. - *

- *

- * Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on - * the query. This translates into SQL that uses limit offset, rownum or row_number - * function to limit the result set. - *

- * - * @param pageIndex - * The zero based index of the page. - * @param pageSize - * The number of beans to return per page. - * @return The PagedList - */ - public PagedList findPagedList(int pageIndex, int pageSize); - - /** - * Add some filter predicate expressions to the many property. - */ - public ExpressionList filterMany(String prop); - - /** - * Specify specific properties to fetch on the main/root bean (aka partial - * object). - * - * @see Query#select(String) - */ - public Query select(String properties); - - /** - * Set the first row to fetch. - * - * @see Query#setFirstRow(int) - */ - public Query setFirstRow(int firstRow); - - /** - * Set the maximum number of rows to fetch. - * - * @see Query#setMaxRows(int) - */ - public Query setMaxRows(int maxRows); - - /** - * Set the name of the property which values become the key of a map. - * - * @see Query#setMapKey(String) - */ - public Query setMapKey(String mapKey); - - /** - * Set to true to use the query for executing this query. - * - * @see Query#setUseCache(boolean) - */ - public Query setUseCache(boolean useCache); - - /** - * Add expressions to the having clause. - *

- * The having clause is only used for queries based on raw sql (via SqlSelect - * annotation etc). - *

- */ - public ExpressionList having(); - - /** - * Add another expression to the where clause. - */ - public ExpressionList where(); - - /** - * Add an Expression to the list. - *

- * This returns the list so that add() can be chained. - *

- * - *
-   * Query<Customer> query = Ebean.createQuery(Customer.class);
-   * query.where()
-   *     .like("name","Rob%")
-   *     .eq("status", Customer.ACTIVE);
-   * List<Customer> list = query.findList();
-   * ...
-   * 
- */ - public ExpressionList add(Expression expr); - - /** - * Add a list of Expressions to this ExpressionList.s - */ - public ExpressionList addAll(ExpressionList exprList); - - /** - * Equal To - property is equal to a given value. - */ - public ExpressionList eq(String propertyName, Object value); - - /** - * Not Equal To - property not equal to the given value. - */ - public ExpressionList ne(String propertyName, Object value); - - /** - * Case Insensitive Equal To - property equal to the given value (typically - * using a lower() function to make it case insensitive). - */ - public ExpressionList ieq(String propertyName, String value); - - /** - * Between - property between the two given values. - */ - public ExpressionList between(String propertyName, Object value1, Object value2); - - /** - * Between - value between the two properties. - */ - public ExpressionList betweenProperties(String lowProperty, String highProperty, Object value); - - /** - * Greater Than - property greater than the given value. - */ - public ExpressionList gt(String propertyName, Object value); - - /** - * Greater Than or Equal to - property greater than or equal to the given - * value. - */ - public ExpressionList ge(String propertyName, Object value); - - /** - * Less Than - property less than the given value. - */ - public ExpressionList lt(String propertyName, Object value); - - /** - * Less Than or Equal to - property less than or equal to the given value. - */ - public ExpressionList le(String propertyName, Object value); - - /** - * Is Null - property is null. - */ - public ExpressionList isNull(String propertyName); - - /** - * Is Not Null - property is not null. - */ - public ExpressionList isNotNull(String propertyName); - - /** - * A "Query By Example" type of expression. - *

- * Pass in an example entity and for each non-null scalar properties an - * expression is added. - *

- *

- * By Default this case sensitive, will ignore numeric zero values and will - * use a Like for string values (you must put in your own wildcards). - *

- *

- * To get control over the options you can create an ExampleExpression and set - * those options such as case insensitive etc. - *

- * - *
-   * // create an example bean and set the properties
-   * // with the query parameters you want
-   * 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 - * - *
-   * 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<Customer> list = Ebean.find(Customer.class).where().add(qbe).findList();
-   * 
- */ - public ExpressionList exampleLike(Object example); - - /** - * Case insensitive version of {@link #exampleLike(Object)} - */ - public ExpressionList iexampleLike(Object example); - - /** - * Like - property like value where the value contains the SQL wild card - * characters % (percentage) and _ (underscore). - */ - public ExpressionList like(String propertyName, String value); - - /** - * Case insensitive Like - property like value where the value contains the - * SQL wild card characters % (percentage) and _ (underscore). Typically uses - * a lower() function to make the expression case insensitive. - */ - public ExpressionList ilike(String propertyName, String value); - - /** - * Starts With - property like value%. - */ - public ExpressionList startsWith(String propertyName, String value); - - /** - * Case insensitive Starts With - property like value%. Typically uses a - * lower() function to make the expression case insensitive. - */ - public ExpressionList istartsWith(String propertyName, String value); - - /** - * Ends With - property like %value. - */ - public ExpressionList endsWith(String propertyName, String value); - - /** - * Case insensitive Ends With - property like %value. Typically uses a lower() - * function to make the expression case insensitive. - */ - public ExpressionList iendsWith(String propertyName, String value); - - /** - * Contains - property like %value%. - */ - public ExpressionList contains(String propertyName, String value); - - /** - * Case insensitive Contains - property like %value%. Typically uses a lower() - * function to make the expression case insensitive. - */ - public ExpressionList icontains(String propertyName, String value); - - /** - * In - using a subQuery. - */ - public ExpressionList in(String propertyName, Query subQuery); - - /** - * In - property has a value in the array of values. - */ - public ExpressionList in(String propertyName, Object... values); - - /** - * In - property has a value in the collection of values. - */ - public ExpressionList in(String propertyName, Collection values); - - /** - * Exists expression - */ - public ExpressionList exists(Query subQuery); - - /** - * Not exists expression - */ - public ExpressionList notExists(Query subQuery); - - /** - * Id IN a list of id values. - */ - public ExpressionList idIn(List idValues); - - /** - * Id Equal to - ID property is equal to the value. - */ - public ExpressionList idEq(Object value); - - /** - * All Equal - Map containing property names and their values. - *

- * Expression where all the property names in the map are equal to the - * corresponding value. - *

- * - * @param propertyMap - * a map keyed by property names. - */ - public ExpressionList allEq(Map propertyMap); - - /** - * Add raw expression with a single parameter. - *

- * The raw expression should contain a single ? at the location of the - * parameter. - *

- *

- * When properties in the clause are fully qualified as table-column names - * then they are not translated. logical property name names (not fully - * qualified) will still be translated to their physical name. - *

- */ - public ExpressionList raw(String raw, Object value); - - /** - * Add raw expression with an array of parameters. - *

- * The raw expression should contain the same number of ? as there are - * parameters. - *

- *

- * When properties in the clause are fully qualified as table-column names - * then they are not translated. logical property name names (not fully - * qualified) will still be translated to their physical name. - *

- */ - public ExpressionList raw(String raw, Object[] values); - - /** - * Add raw expression with no parameters. - *

- * When properties in the clause are fully qualified as table-column names - * then they are not translated. logical property name names (not fully - * qualified) will still be translated to their physical name. - *

- */ - public ExpressionList raw(String raw); - - /** - * And - join two expressions with a logical and. - */ - public ExpressionList and(Expression expOne, Expression expTwo); - - /** - * Or - join two expressions with a logical or. - */ - public ExpressionList or(Expression expOne, Expression expTwo); - - /** - * Negate the expression (prefix it with NOT). - */ - public ExpressionList not(Expression exp); - - /** - * Return a list of expressions that will be joined by AND's. - */ - public Junction conjunction(); - - /** - * Return a list of expressions that will be joined by OR's. - */ - public Junction disjunction(); - - /** - * End a Conjunction or Disjunction returning the parent expression list. - *

- * Alternatively you can always use where() to return the top level expression - * list. - *

- */ - public ExpressionList endJunction(); - -} +package com.avaje.ebean; + +import com.avaje.ebean.text.PathProperties; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * List of Expressions that make up a where or having clause. + *

+ * An ExpressionList is returned from {@link Query#where()}. + *

+ *

+ * The ExpressionList has a list of convenience methods that create the standard + * expressions and add them to this list. + *

+ *

+ * The ExpressionList also duplicates methods that are found on the Query such + * as findList() and orderBy(). The purpose of these methods is provide a fluid + * API. The upside of this approach is that you can build and execute a query + * via chained methods. The down side is that this ExpressionList object has + * more methods than you would initially expect (the ones duplicated from + * Query). + *

+ * + * @see Query#where() + */ +public interface ExpressionList extends Serializable { + + /** + * Return the query that owns this expression list. + *

+ * This is a convenience method solely to support a fluid API where the + * methods are chained together. Adding expressions returns this expression + * list and this method can be used after that to return back the original + * query so that further things can be added to it. + *

+ */ + Query query(); + + /** + * Set the order by clause replacing the existing order by clause if there is + * one. + *

+ * This follows SQL syntax using commas between each property with the + * optional asc and desc keywords representing ascending and descending order + * respectively. + *

+ *

+ * This is EXACTLY the same as {@link #orderBy(String)}. + *

+ */ + Query order(String orderByClause); + + /** + * Return the OrderBy so that you can append an ascending or descending + * property to the order by clause. + *

+ * This will never return a null. If no order by clause exists then an 'empty' + * OrderBy object is returned. + *

+ */ + OrderBy order(); + + /** + * Return the OrderBy so that you can append an ascending or descending + * property to the order by clause. + *

+ * This will never return a null. If no order by clause exists then an 'empty' + * OrderBy object is returned. + *

+ */ + OrderBy orderBy(); + + /** + * 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); + + /** + * Apply the path properties to the query replacing the select and fetch clauses. + */ + Query apply(PathProperties pathProperties); + + /** + * Execute the query iterating over the results. + * + * @see Query#findIterate() + */ + QueryIterator findIterate(); + + /** + * Execute the query process the beans one at a time. + * + * @see Query#findEach(QueryEachConsumer) + */ + void findEach(QueryEachConsumer consumer); + + /** + * Execute the query processing the beans one at a time with the ability to + * stop processing before reading all the beans. + * + * @see Query#findEachWhile(QueryEachWhileConsumer) + */ + void findEachWhile(QueryEachWhileConsumer consumer); + + /** + * Deprecated in favor of #findEachWhile which is functionally exactly the same + * but has a much better name. + * + * @deprecated + */ + void findVisit(QueryResultVisitor visitor); + + /** + * 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(); + + /** + * Return the count of entities this query should return. + *

+ * This is the number of 'top level' or 'root level' entities. + *

+ */ + int findRowCount(); + + /** + * Execute the query returning a set. + * + * @see Query#findSet() + */ + Set findSet(); + + /** + * Execute the query returning a map. + * + * @see Query#findMap() + */ + Map findMap(); + + /** + * Return a typed map specifying the key property and type. + */ + Map findMap(String keyProperty, Class keyType); + + /** + * Execute the query returning a single bean. + * + * @see Query#findUnique() + */ + T findUnique(); + + /** + * 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 + */ + FutureRowCount findFutureRowCount(); + + /** + * Execute find Id's 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 list of Id's + */ + FutureIds findFutureIds(); + + /** + * Execute find list 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 list result of the query + */ + FutureList findFutureList(); + + /** + * Return a PagedList for this query. + *

+ * The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and + * {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to + * {@link Query#findFutureRowCount()} to determine total row count, total page count etc. + *

+ *

+ * Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on + * the query. This translates into SQL that uses limit offset, rownum or row_number + * function to limit the result set. + *

+ * + * @param pageIndex + * The zero based index of the page. + * @param pageSize + * The number of beans to return per page. + * @return The PagedList + */ + PagedList findPagedList(int pageIndex, int pageSize); + + /** + * Add some filter predicate expressions to the many property. + */ + ExpressionList filterMany(String prop); + + /** + * Specify specific properties to fetch on the main/root bean (aka partial + * object). + * + * @see Query#select(String) + */ + Query select(String properties); + + /** + * 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); + + /** + * Add expressions to the having clause. + *

+ * The having clause is only used for queries based on raw sql (via SqlSelect + * annotation etc). + *

+ */ + ExpressionList having(); + + /** + * Add another expression to the where clause. + */ + ExpressionList where(); + + /** + * Add an Expression to the list. + *

+ * This returns the list so that add() can be chained. + *

+ * + *
+   * Query<Customer> query = Ebean.createQuery(Customer.class);
+   * query.where()
+   *     .like("name","Rob%")
+   *     .eq("status", Customer.ACTIVE);
+   * List<Customer> list = query.findList();
+   * ...
+   * 
+ */ + ExpressionList add(Expression expr); + + /** + * Add a list of Expressions to this ExpressionList.s + */ + ExpressionList addAll(ExpressionList exprList); + + /** + * Equal To - property is equal to a given value. + */ + ExpressionList eq(String propertyName, Object value); + + /** + * Not Equal To - property not equal to the given value. + */ + ExpressionList ne(String propertyName, Object value); + + /** + * Case Insensitive Equal To - property equal to the given value (typically + * using a lower() function to make it case insensitive). + */ + ExpressionList ieq(String propertyName, String value); + + /** + * Between - property between the two given values. + */ + ExpressionList between(String propertyName, Object value1, Object value2); + + /** + * Between - value between the two properties. + */ + ExpressionList betweenProperties(String lowProperty, String highProperty, Object value); + + /** + * Greater Than - property greater than the given value. + */ + ExpressionList gt(String propertyName, Object value); + + /** + * Greater Than or Equal to - property greater than or equal to the given + * value. + */ + ExpressionList ge(String propertyName, Object value); + + /** + * Less Than - property less than the given value. + */ + ExpressionList lt(String propertyName, Object value); + + /** + * Less Than or Equal to - property less than or equal to the given value. + */ + ExpressionList le(String propertyName, Object value); + + /** + * Is Null - property is null. + */ + ExpressionList isNull(String propertyName); + + /** + * Is Not Null - property is not null. + */ + ExpressionList isNotNull(String propertyName); + + /** + * A "Query By Example" type of expression. + *

+ * Pass in an example entity and for each non-null scalar properties an + * expression is added. + *

+ *

+ * By Default this case sensitive, will ignore numeric zero values and will + * use a Like for string values (you must put in your own wildcards). + *

+ *

+ * To get control over the options you can create an ExampleExpression and set + * those options such as case insensitive etc. + *

+ * + *
+   * // create an example bean and set the properties
+   * // with the query parameters you want
+   * 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 + * + *
+   * 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<Customer> list = Ebean.find(Customer.class).where().add(qbe).findList();
+   * 
+ */ + ExpressionList exampleLike(Object example); + + /** + * Case insensitive version of {@link #exampleLike(Object)} + */ + ExpressionList iexampleLike(Object example); + + /** + * Like - property like value where the value contains the SQL wild card + * characters % (percentage) and _ (underscore). + */ + ExpressionList like(String propertyName, String value); + + /** + * Case insensitive Like - property like value where the value contains the + * SQL wild card characters % (percentage) and _ (underscore). Typically uses + * a lower() function to make the expression case insensitive. + */ + ExpressionList ilike(String propertyName, String value); + + /** + * Starts With - property like value%. + */ + ExpressionList startsWith(String propertyName, String value); + + /** + * Case insensitive Starts With - property like value%. Typically uses a + * lower() function to make the expression case insensitive. + */ + ExpressionList istartsWith(String propertyName, String value); + + /** + * Ends With - property like %value. + */ + ExpressionList endsWith(String propertyName, String value); + + /** + * Case insensitive Ends With - property like %value. Typically uses a lower() + * function to make the expression case insensitive. + */ + ExpressionList iendsWith(String propertyName, String value); + + /** + * Contains - property like %value%. + */ + ExpressionList contains(String propertyName, String value); + + /** + * Case insensitive Contains - property like %value%. Typically uses a lower() + * function to make the expression case insensitive. + */ + ExpressionList icontains(String propertyName, String value); + + /** + * In - using a subQuery. + */ + ExpressionList in(String propertyName, Query subQuery); + + /** + * In - property has a value in the array of values. + */ + ExpressionList in(String propertyName, Object... values); + + /** + * In - property has a value in the collection of values. + */ + ExpressionList in(String propertyName, Collection values); + + /** + * Exists expression + */ + ExpressionList exists(Query subQuery); + + /** + * Not exists expression + */ + ExpressionList notExists(Query subQuery); + + /** + * Id IN a list of id values. + */ + ExpressionList idIn(List idValues); + + /** + * Id Equal to - ID property is equal to the value. + */ + ExpressionList idEq(Object value); + + /** + * All Equal - Map containing property names and their values. + *

+ * Expression where all the property names in the map are equal to the + * corresponding value. + *

+ * + * @param propertyMap + * a map keyed by property names. + */ + ExpressionList allEq(Map propertyMap); + + /** + * Add raw expression with a single parameter. + *

+ * The raw expression should contain a single ? at the location of the + * parameter. + *

+ *

+ * When properties in the clause are fully qualified as table-column names + * then they are not translated. logical property name names (not fully + * qualified) will still be translated to their physical name. + *

+ */ + ExpressionList raw(String raw, Object value); + + /** + * Add raw expression with an array of parameters. + *

+ * The raw expression should contain the same number of ? as there are + * parameters. + *

+ *

+ * When properties in the clause are fully qualified as table-column names + * then they are not translated. logical property name names (not fully + * qualified) will still be translated to their physical name. + *

+ */ + ExpressionList raw(String raw, Object[] values); + + /** + * Add raw expression with no parameters. + *

+ * When properties in the clause are fully qualified as table-column names + * then they are not translated. logical property name names (not fully + * qualified) will still be translated to their physical name. + *

+ */ + ExpressionList raw(String raw); + + /** + * And - join two expressions with a logical and. + */ + ExpressionList and(Expression expOne, Expression expTwo); + + /** + * Or - join two expressions with a logical or. + */ + ExpressionList or(Expression expOne, Expression expTwo); + + /** + * Negate the expression (prefix it with NOT). + */ + ExpressionList not(Expression exp); + + /** + * Return a list of expressions that will be joined by AND's. + */ + Junction conjunction(); + + /** + * Return a list of expressions that will be joined by OR's. + */ + Junction disjunction(); + + /** + * End a Conjunction or Disjunction returning the parent expression list. + *

+ * Alternatively you can always use where() to return the top level expression + * list. + *

+ */ + ExpressionList endJunction(); + +} diff --git a/src/main/java/com/avaje/ebean/FetchConfig.java b/src/main/java/com/avaje/ebean/FetchConfig.java index db5527105..56a6ab6dc 100644 --- a/src/main/java/com/avaje/ebean/FetchConfig.java +++ b/src/main/java/com/avaje/ebean/FetchConfig.java @@ -1,252 +1,252 @@ -package com.avaje.ebean; - -import java.io.Serializable; - -/** - * Defines the configuration options for a "query fetch" or a - * "lazy loading fetch". This gives you the ability to use multiple smaller - * queries to populate an object graph as opposed to a single large query. - *

- * The primary goal is to provide efficient ways of loading complex object - * graphs avoiding SQL Cartesian product and issues around populating object - * graphs that have multiple *ToMany relationships. - *

- *

- * It also provides the ability to control the lazy loading queries (batch size, - * selected properties and fetches) to avoid N+1 queries etc. - *

- * There can also be cases loading across a single OneToMany where 2 SQL queries - * using Ebean FetchConfig.query() can be more efficient than one SQL query. - * When the "One" side is wide (lots of columns) and the cardinality difference - * is high (a lot of "Many" beans per "One" bean) then this can be more - * efficient loaded as 2 SQL queries. - *

- * - *
- * // Normal fetch join results in a single SQL query
- * List<Order> list = Ebean.find(Order.class).fetch("details").findList();
- * 
- * // 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 - *

- * - *
- * // This will use 2 SQL queries to build this object graph
- * List<Order> list =
- *     Ebean.find(Order.class)
- *         .fetch("details", new FetchConfig().query())
- *         .findList();
- * 
- * // query 1) find order
- * // query 2) find orderDetails where order.id in (?,?...) // first 100 order id's
- * 
- *

- * Example: Using 2 "query joins" - *

- * - *
- * // This will use 3 SQL queries to build this object graph
- * List<Order> list =
- *     Ebean.find(Order.class)
- *         .fetch("details", new FetchConfig().query())
- *         .fetch("customer", new FetchConfig().queryFirst(5))
- *         .findList();
- * 
- * // query 1) find order
- * // query 2) find orderDetails where order.id in (?,?...) // first 100 order id's
- * // query 3) find customer where id in (?,?,?,?,?) // first 5 customers
- * 
- *

- * Example: Using "query joins" and partial objects - *

- * - *
- * // This will use 3 SQL queries to build this object graph
- * List<Order> list =
- *     Ebean.find(Order.class)
- *         .select("status, shipDate")
- *         .fetch("details", "quantity, price", new FetchConfig().query())
- *         .fetch("details.product", "sku, name")
- *         .fetch("customer", "name", new FetchConfig().queryFirst(5))
- *         .fetch("customer.contacts")
- *         .fetch("customer.shippingAddress")
- *         .findList();
- * 
- * // query 1) find order (status, shipDate)
- * // query 2) find orderDetail (quantity, price) fetch product (sku, name) where
- * // order.id in (?,? ...)
- * // query 3) find customer (name) fetch contacts (*) fetch shippingAddress (*)
- * // where id in (?,?,?,?,?)
- * 
- * // Note: the fetch of "details.product" is automatically included into the
- * // fetch of "details"
- * //
- * // Note: the fetch of "customer.contacts" and "customer.shippingAddress"
- * // are automatically included in the fetch of "customer"
- * 
- *

- * You can use query() and lazy together on a single join. The query is executed - * immediately and the lazy defines the batch size to use for further lazy - * loading (if lazy loading is invoked). - *

- * - *
- * List<Order> list =
- *     Ebean.find(Order.class)
- *         .fetch("customer", new FetchConfig().query(10).lazy(5))
- *         .findList();
- * 
- * // query 1) find order
- * // query 2) find customer where id in (?,?,?,?,?,?,?,?,?,?) // first 10 customers
- * // .. then if lazy loading of customers is invoked
- * // .. use a batch size of 5 to load the customers
- * 
- * 
- * - *

- * Example of controlling the lazy loading query: - *

- *

- * This gives us the ability to optimise the lazy loading query for a given use - * case. - *

- * - *
- * List<Order> list = Ebean.find(Order.class)
- *   .fetch("customer","name", new FetchConfig().lazy(5))
- *   .fetch("customer.contacts","contactName, phone, email")
- *   .fetch("customer.shippingAddress")
- *   .where().eq("status",Order.Status.NEW)
- *   .findList();
- * 
- * // 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 (?,?,?,?,?)
- * 
- * 
- * - * @author mario - * @author rbygrave - */ -public class FetchConfig implements Serializable { - - private static final long serialVersionUID = 1L; - - private int lazyBatchSize = -1; - - private int queryBatchSize = -1; - - private boolean queryAll; - - /** - * Construct the fetch configuration object. - */ - public FetchConfig() { - } - - /** - * Specify that this path should be lazy loaded using the default batch load - * size. - */ - public FetchConfig lazy() { - this.lazyBatchSize = 0; - this.queryAll = false; - return this; - } - - /** - * Specify that this path should be lazy loaded with a specified batch size. - * - * @param lazyBatchSize - * the batch size for lazy loading - */ - public FetchConfig lazy(int lazyBatchSize) { - this.lazyBatchSize = lazyBatchSize; - this.queryAll = false; - return this; - } - - /** - * Eagerly fetch the beans in this path as a separate query (rather than as - * part of the main query). - *

- * This will use the default batch size for separate query which is 100. - *

- */ - public FetchConfig query() { - this.queryBatchSize = 0; - this.queryAll = true; - return this; - } - - /** - * Eagerly fetch the beans in this path as a separate query (rather than as - * part of the main query). - *

- * The queryBatchSize is the number of parent id's that this separate query - * will load per batch. - *

- *

- * This will load all beans on this path eagerly unless a {@link #lazy(int)} - * is also used. - *

- * - * @param queryBatchSize - * the batch size used to load beans on this path - */ - public FetchConfig query(int queryBatchSize) { - this.queryBatchSize = queryBatchSize; - // queryAll true as long as a lazy batch size has not already been set - this.queryAll = (lazyBatchSize == -1); - return this; - } - - /** - * Eagerly fetch the first batch of beans on this path. - * This is similar to {@link #query(int)} but only fetches the first batch. - *

- * If there are more parent beans than the batch size then they will not be - * loaded eagerly but instead use lazy loading. - *

- * - * @param queryBatchSize - * the number of parent beans this path is populated for - */ - public FetchConfig queryFirst(int queryBatchSize) { - this.queryBatchSize = queryBatchSize; - this.queryAll = false; - return this; - } - - /** - * Return the batch size for lazy loading. - */ - public int getLazyBatchSize() { - return lazyBatchSize; - } - - /** - * Return the batch size for separate query load. - */ - public int getQueryBatchSize() { - return queryBatchSize; - } - - /** - * Return true if the query fetch should fetch 'all' rather than just the - * 'first' batch. - */ - public boolean isQueryAll() { - return queryAll; - } - -} +package com.avaje.ebean; + +import java.io.Serializable; + +/** + * Defines the configuration options for a "query fetch" or a + * "lazy loading fetch". This gives you the ability to use multiple smaller + * queries to populate an object graph as opposed to a single large query. + *

+ * The primary goal is to provide efficient ways of loading complex object + * graphs avoiding SQL Cartesian product and issues around populating object + * graphs that have multiple *ToMany relationships. + *

+ *

+ * It also provides the ability to control the lazy loading queries (batch size, + * selected properties and fetches) to avoid N+1 queries etc. + *

+ * There can also be cases loading across a single OneToMany where 2 SQL queries + * using Ebean FetchConfig.query() can be more efficient than one SQL query. + * When the "One" side is wide (lots of columns) and the cardinality difference + * is high (a lot of "Many" beans per "One" bean) then this can be more + * efficient loaded as 2 SQL queries. + *

+ * + *
+ * // Normal fetch join results in a single SQL query
+ * List<Order> list = Ebean.find(Order.class).fetch("details").findList();
+ * 
+ * // 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 + *

+ * + *
+ * // This will use 2 SQL queries to build this object graph
+ * List<Order> list =
+ *     Ebean.find(Order.class)
+ *         .fetch("details", new FetchConfig().query())
+ *         .findList();
+ * 
+ * // query 1) find order
+ * // query 2) find orderDetails where order.id in (?,?...) // first 100 order id's
+ * 
+ *

+ * Example: Using 2 "query joins" + *

+ * + *
+ * // This will use 3 SQL queries to build this object graph
+ * List<Order> list =
+ *     Ebean.find(Order.class)
+ *         .fetch("details", new FetchConfig().query())
+ *         .fetch("customer", new FetchConfig().queryFirst(5))
+ *         .findList();
+ * 
+ * // query 1) find order
+ * // query 2) find orderDetails where order.id in (?,?...) // first 100 order id's
+ * // query 3) find customer where id in (?,?,?,?,?) // first 5 customers
+ * 
+ *

+ * Example: Using "query joins" and partial objects + *

+ * + *
+ * // This will use 3 SQL queries to build this object graph
+ * List<Order> list =
+ *     Ebean.find(Order.class)
+ *         .select("status, shipDate")
+ *         .fetch("details", "quantity, price", new FetchConfig().query())
+ *         .fetch("details.product", "sku, name")
+ *         .fetch("customer", "name", new FetchConfig().queryFirst(5))
+ *         .fetch("customer.contacts")
+ *         .fetch("customer.shippingAddress")
+ *         .findList();
+ * 
+ * // query 1) find order (status, shipDate)
+ * // query 2) find orderDetail (quantity, price) fetch product (sku, name) where
+ * // order.id in (?,? ...)
+ * // query 3) find customer (name) fetch contacts (*) fetch shippingAddress (*)
+ * // where id in (?,?,?,?,?)
+ * 
+ * // Note: the fetch of "details.product" is automatically included into the
+ * // fetch of "details"
+ * //
+ * // Note: the fetch of "customer.contacts" and "customer.shippingAddress"
+ * // are automatically included in the fetch of "customer"
+ * 
+ *

+ * You can use query() and lazy together on a single join. The query is executed + * immediately and the lazy defines the batch size to use for further lazy + * loading (if lazy loading is invoked). + *

+ * + *
+ * List<Order> list =
+ *     Ebean.find(Order.class)
+ *         .fetch("customer", new FetchConfig().query(10).lazy(5))
+ *         .findList();
+ * 
+ * // query 1) find order
+ * // query 2) find customer where id in (?,?,?,?,?,?,?,?,?,?) // first 10 customers
+ * // .. then if lazy loading of customers is invoked
+ * // .. use a batch size of 5 to load the customers
+ * 
+ * 
+ * + *

+ * Example of controlling the lazy loading query: + *

+ *

+ * This gives us the ability to optimise the lazy loading query for a given use + * case. + *

+ * + *
+ * List<Order> list = Ebean.find(Order.class)
+ *   .fetch("customer","name", new FetchConfig().lazy(5))
+ *   .fetch("customer.contacts","contactName, phone, email")
+ *   .fetch("customer.shippingAddress")
+ *   .where().eq("status",Order.Status.NEW)
+ *   .findList();
+ * 
+ * // 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 (?,?,?,?,?)
+ * 
+ * 
+ * + * @author mario + * @author rbygrave + */ +public class FetchConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + private int lazyBatchSize = -1; + + private int queryBatchSize = -1; + + private boolean queryAll; + + /** + * Construct the fetch configuration object. + */ + public FetchConfig() { + } + + /** + * Specify that this path should be lazy loaded using the default batch load + * size. + */ + public FetchConfig lazy() { + this.lazyBatchSize = 0; + this.queryAll = false; + return this; + } + + /** + * Specify that this path should be lazy loaded with a specified batch size. + * + * @param lazyBatchSize + * the batch size for lazy loading + */ + public FetchConfig lazy(int lazyBatchSize) { + this.lazyBatchSize = lazyBatchSize; + this.queryAll = false; + return this; + } + + /** + * Eagerly fetch the beans in this path as a separate query (rather than as + * part of the main query). + *

+ * This will use the default batch size for separate query which is 100. + *

+ */ + public FetchConfig query() { + this.queryBatchSize = 0; + this.queryAll = true; + return this; + } + + /** + * Eagerly fetch the beans in this path as a separate query (rather than as + * part of the main query). + *

+ * The queryBatchSize is the number of parent id's that this separate query + * will load per batch. + *

+ *

+ * This will load all beans on this path eagerly unless a {@link #lazy(int)} + * is also used. + *

+ * + * @param queryBatchSize + * the batch size used to load beans on this path + */ + public FetchConfig query(int queryBatchSize) { + this.queryBatchSize = queryBatchSize; + // queryAll true as long as a lazy batch size has not already been set + this.queryAll = (lazyBatchSize == -1); + return this; + } + + /** + * Eagerly fetch the first batch of beans on this path. + * This is similar to {@link #query(int)} but only fetches the first batch. + *

+ * If there are more parent beans than the batch size then they will not be + * loaded eagerly but instead use lazy loading. + *

+ * + * @param queryBatchSize + * the number of parent beans this path is populated for + */ + public FetchConfig queryFirst(int queryBatchSize) { + this.queryBatchSize = queryBatchSize; + this.queryAll = false; + return this; + } + + /** + * Return the batch size for lazy loading. + */ + public int getLazyBatchSize() { + return lazyBatchSize; + } + + /** + * Return the batch size for separate query load. + */ + public int getQueryBatchSize() { + return queryBatchSize; + } + + /** + * Return true if the query fetch should fetch 'all' rather than just the + * 'first' batch. + */ + public boolean isQueryAll() { + return queryAll; + } + +} diff --git a/src/main/java/com/avaje/ebean/Filter.java b/src/main/java/com/avaje/ebean/Filter.java index a01b85294..977b61699 100644 --- a/src/main/java/com/avaje/ebean/Filter.java +++ b/src/main/java/com/avaje/ebean/Filter.java @@ -90,98 +90,98 @@ public interface Filter { * Refer to {@link Ebean#sort(List, String)} for more detail. *

*/ - public Filter sort(String sortByClause); + Filter sort(String sortByClause); /** * Specify the maximum number of rows/elements to return. */ - public Filter maxRows(int maxRows); + Filter maxRows(int maxRows); /** * Equal To - property equal to the given value. */ - public Filter eq(String prop, Object value); + Filter eq(String prop, Object value); /** * Not Equal To - property not equal to the given value. */ - public Filter ne(String propertyName, Object value); + Filter ne(String propertyName, Object value); /** * Case Insensitive Equal To. */ - public Filter ieq(String propertyName, String value); + Filter ieq(String propertyName, String value); /** * Between - property between the two given values. */ - public Filter between(String propertyName, Object value1, Object value2); + Filter between(String propertyName, Object value1, Object value2); /** * Greater Than - property greater than the given value. */ - public Filter gt(String propertyName, Object value); + Filter gt(String propertyName, Object value); /** * Greater Than or Equal to - property greater than or equal to the given * value. */ - public Filter ge(String propertyName, Object value); + Filter ge(String propertyName, Object value); /** * Less Than - property less than the given value. */ - public Filter lt(String propertyName, Object value); + Filter lt(String propertyName, Object value); /** * Less Than or Equal to - property less than or equal to the given value. */ - public Filter le(String propertyName, Object value); + Filter le(String propertyName, Object value); /** * Is Null - property is null. */ - public Filter isNull(String propertyName); + Filter isNull(String propertyName); /** * Is Not Null - property is not null. */ - public Filter isNotNull(String propertyName); + Filter isNotNull(String propertyName); /** * Starts With. */ - public Filter startsWith(String propertyName, String value); + Filter startsWith(String propertyName, String value); /** * Case insensitive Starts With. */ - public Filter istartsWith(String propertyName, String value); + Filter istartsWith(String propertyName, String value); /** * Ends With. */ - public Filter endsWith(String propertyName, String value); + Filter endsWith(String propertyName, String value); /** * Case insensitive Ends With. */ - public Filter iendsWith(String propertyName, String value); + Filter iendsWith(String propertyName, String value); /** * Contains - property contains the string "value". */ - public Filter contains(String propertyName, String value); + Filter contains(String propertyName, String value); /** * Case insensitive Contains. */ - public Filter icontains(String propertyName, String value); + Filter icontains(String propertyName, String value); /** * In - property has a value contained in the set of values. */ - public Filter in(String propertyName, Set values); + Filter in(String propertyName, Set values); /** * Apply the filter to the list returning a new list of the matching elements @@ -192,6 +192,6 @@ public interface Filter { * * @return Returns a new list with the sorting and filters applied. */ - public List filter(List sourceList); + List filter(List sourceList); } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/FutureIds.java b/src/main/java/com/avaje/ebean/FutureIds.java index 9bd07c9a1..ab8d80a5d 100644 --- a/src/main/java/com/avaje/ebean/FutureIds.java +++ b/src/main/java/com/avaje/ebean/FutureIds.java @@ -1,34 +1,34 @@ -package com.avaje.ebean; - -import java.util.List; -import java.util.concurrent.Future; - -/** - * FutureIds represents the result of a background query execution for the Id's. - *

- * 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> { - - /** - * Returns the original query used to fetch the Id's. - */ - public 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. - *

- */ - public List getPartialIds(); -} +package com.avaje.ebean; + +import java.util.List; +import java.util.concurrent.Future; + +/** + * FutureIds represents the result of a background query execution for the Id's. + *

+ * 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> { + + /** + * Returns the original query used to fetch the Id's. + */ + 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/FutureList.java b/src/main/java/com/avaje/ebean/FutureList.java index cb4efcb7d..847de9b2a 100644 --- a/src/main/java/com/avaje/ebean/FutureList.java +++ b/src/main/java/com/avaje/ebean/FutureList.java @@ -1,76 +1,76 @@ -package com.avaje.ebean; - -import javax.persistence.PersistenceException; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -/** - * FutureList represents the result of a background query execution that will - * return a list of entities. - *

- * It extends the java.util.concurrent.Future with the ability to cancel the - * query, check if it is finished and get the resulting list waiting for the - * query to finish (ie. the standard features of java.util.concurrent.Future). - *

- *

- * A simple example: - *

- * - *
{@code
- *  // create a query to find all orders
- * Query query = Ebean.find(Order.class);
- * 
- *  // execute the query in a background thread
- *  // immediately returning the futureList
- * FutureList futureList = query.findFutureList();
- * 
- *  // do something else ... 
- * 
- * if (!futureList.isDone()){
- * 	// we can cancel the query execution. This will cancel
- * // the underlying query if that is supported by the JDBC
- * // driver and database
- * 	futureList.cancel(true);
- * }
- * 
- * 
- * if (!futureList.isCancelled()){
- * 	// wait for the query to finish and return the list
- * 	List list = futureList.get();
- * 	...
- * }
- * 
- * }
- */ -public interface FutureList extends Future> { - - /** - * Return the query that is being executed by a background thread. - */ - public Query getQuery(); - - /** - * Same as {@link #get()} but wraps InterruptedException and ExecutionException in the - * unchecked PersistenceException. - * - * @return The query list result - * - * @throws PersistenceException when a InterruptedException or ExecutionException occurs. - */ - public List getUnchecked(); - - /** - * Same as {@link #get(long, java.util.concurrent.TimeUnit)} but wraps InterruptedException - * and ExecutionException in the unchecked PersistenceException. - * - * @return The query list result - * - * @throws TimeoutException if the wait timed out - * @throws PersistenceException if a InterruptedException or ExecutionException occurs. - */ - public List getUnchecked(long timeout, TimeUnit unit) throws TimeoutException; - -} +package com.avaje.ebean; + +import javax.persistence.PersistenceException; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * FutureList represents the result of a background query execution that will + * return a list of entities. + *

+ * It extends the java.util.concurrent.Future with the ability to cancel the + * query, check if it is finished and get the resulting list waiting for the + * query to finish (ie. the standard features of java.util.concurrent.Future). + *

+ *

+ * A simple example: + *

+ * + *
{@code
+ *  // create a query to find all orders
+ * Query query = Ebean.find(Order.class);
+ * 
+ *  // execute the query in a background thread
+ *  // immediately returning the futureList
+ * FutureList futureList = query.findFutureList();
+ * 
+ *  // do something else ... 
+ * 
+ * if (!futureList.isDone()){
+ * 	// we can cancel the query execution. This will cancel
+ * // the underlying query if that is supported by the JDBC
+ * // driver and database
+ * 	futureList.cancel(true);
+ * }
+ * 
+ * 
+ * if (!futureList.isCancelled()){
+ * 	// wait for the query to finish and return the list
+ * 	List list = futureList.get();
+ * 	...
+ * }
+ * 
+ * }
+ */ +public interface FutureList extends Future> { + + /** + * Return the query that is being executed by a background thread. + */ + Query getQuery(); + + /** + * Same as {@link #get()} but wraps InterruptedException and ExecutionException in the + * unchecked PersistenceException. + * + * @return The query list result + * + * @throws PersistenceException when a InterruptedException or ExecutionException occurs. + */ + List getUnchecked(); + + /** + * Same as {@link #get(long, java.util.concurrent.TimeUnit)} but wraps InterruptedException + * and ExecutionException in the unchecked PersistenceException. + * + * @return The query list result + * + * @throws TimeoutException if the wait timed out + * @throws PersistenceException if a InterruptedException or ExecutionException occurs. + */ + List getUnchecked(long timeout, TimeUnit unit) throws TimeoutException; + +} diff --git a/src/main/java/com/avaje/ebean/FutureRowCount.java b/src/main/java/com/avaje/ebean/FutureRowCount.java index b1fdeae7f..36aa6ab33 100644 --- a/src/main/java/com/avaje/ebean/FutureRowCount.java +++ b/src/main/java/com/avaje/ebean/FutureRowCount.java @@ -1,15 +1,15 @@ -package com.avaje.ebean; - -import java.util.concurrent.Future; - -/** - * Represents the result of a background query execution for the total row count - * for a query. - *

- * It extends the java.util.concurrent.Future. - *

- * - * @author rbygrave - */ -public interface FutureRowCount extends Future { -} +package com.avaje.ebean; + +import java.util.concurrent.Future; + +/** + * Represents the result of a background query execution for the total row count + * for a query. + *

+ * It extends the java.util.concurrent.Future. + *

+ * + * @author rbygrave + */ +public interface FutureRowCount extends Future { +} diff --git a/src/main/java/com/avaje/ebean/OrderBy.java b/src/main/java/com/avaje/ebean/OrderBy.java index 83c2b3404..7ef6ff8ee 100644 --- a/src/main/java/com/avaje/ebean/OrderBy.java +++ b/src/main/java/com/avaje/ebean/OrderBy.java @@ -1,351 +1,351 @@ -package com.avaje.ebean; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Represents an Order By for a Query. - *

- * Is a ordered list of OrderBy.Property objects each specifying a property and - * whether it is ascending or descending order. - *

- *

- * Typically you will not construct an OrderBy yourself but use one that exists - * on the Query object. - *

- */ -public final class OrderBy implements Serializable { - - private static final long serialVersionUID = 9157089257745730539L; - - private transient Query query; - - private final List list; - - /** - * Create an empty OrderBy with no associated query. - */ - public OrderBy() { - this.list = new ArrayList(2); - } - - private OrderBy(List list) { - this.list = list; - } - - /** - * Create an orderBy parsing the order by clause. - *

- * The order by clause follows SQL order by clause with comma's between each - * property and optionally "asc" or "desc" to represent ascending or - * descending order respectively. - *

- */ - public OrderBy(String orderByClause) { - this(null, orderByClause); - } - - /** - * Construct with a given query and order by clause. - */ - public OrderBy(Query query, String orderByClause) { - this.query = query; - this.list = new ArrayList(2); - parse(orderByClause); - } - - /** - * Reverse the ascending/descending order on all the properties. - */ - public void reverse() { - for (int i = 0; i < list.size(); i++) { - list.get(i).reverse(); - } - } - - /** - * Add a property with ascending order to this OrderBy. - */ - public Query asc(String propertyName) { - - list.add(new Property(propertyName, true)); - return query; - } - - /** - * Add a property with descending order to this OrderBy. - */ - public Query desc(String propertyName) { - - list.add(new Property(propertyName, false)); - return query; - } - - /** - * Return a copy of this OrderBy with the path trimmed. - */ - public OrderBy copyWithTrim(String path) { - List newList = new ArrayList(list.size()); - for (int i = 0; i < list.size(); i++) { - newList.add(list.get(i).copyWithTrim(path)); - } - return new OrderBy(newList); - } - - /** - * Return the properties for this OrderBy. - */ - public List getProperties() { - // not returning an Immutable list at this point - return list; - } - - /** - * Return true if this OrderBy does not have any properties. - */ - public boolean isEmpty() { - return list.isEmpty(); - } - - /** - * Return the associated query if there is one. - */ - public Query getQuery() { - return query; - } - - /** - * Associate this OrderBy with a query. - */ - public void setQuery(Query query) { - this.query = query; - } - - /** - * Return a copy of the OrderBy. - */ - public OrderBy copy() { - - OrderBy copy = new OrderBy(); - for (int i = 0; i < list.size(); i++) { - copy.add(list.get(i).copy()); - } - return copy; - } - - /** - * Add a property to the order by. - */ - public void add(Property p) { - list.add(p); - } - - public String toString() { - return list.toString(); - } - - /** - * Returns the OrderBy in string format. - */ - public String toStringFormat() { - if (list.isEmpty()) { - return null; - } - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < list.size(); i++) { - Property property = list.get(i); - if (i > 0) { - sb.append(", "); - } - sb.append(property.toStringFormat()); - } - return sb.toString(); - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof OrderBy)) { - return false; - } - - OrderBy e = (OrderBy) obj; - return e.list.equals(list); - } - - /** - * Return a hash value for this OrderBy. This can be to determine logical - * equality for OrderBy clauses. - */ - public int hashCode() { - return list.hashCode(); - } - - /** - * A property and its ascending descending order. - */ - public static final class Property implements Serializable { - - private static final long serialVersionUID = 1546009780322478077L; - - private String property; - - private boolean ascending; - - public Property(String property, boolean ascending) { - this.property = property; - this.ascending = ascending; - } - - /** - * Return a copy of this Property with the path trimmed. - */ - public Property copyWithTrim(String path) { - return new Property(property.substring(path.length() + 1), ascending); - } - - public int hashCode() { - int hc = property.hashCode(); - hc = hc * 31 + (ascending ? 0 : 1); - return hc; - } - - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof Property)) { - return false; - } - - Property e = (Property) obj; - return e.ascending == ascending - && e.property.equals(property); - } - - public String toString() { - return toStringFormat(); - } - - public String toStringFormat() { - if (ascending) { - return property; - } else { - return property + " desc"; - } - } - - /** - * Reverse the ascending/descending order for this property. - */ - public void reverse() { - this.ascending = !ascending; - } - - /** - * Trim off the pathPrefix. - */ - public void trim(String pathPrefix) { - property = property.substring(pathPrefix.length() + 1); - } - - /** - * Return a copy of this property. - */ - public Property copy() { - return new Property(property, ascending); - } - - /** - * Return the property name. - */ - public String getProperty() { - return property; - } - - /** - * Set the property name. - */ - public void setProperty(String property) { - this.property = property; - } - - /** - * Return true if the order is ascending. - */ - public boolean isAscending() { - return ascending; - } - - /** - * Set to true if the order is ascending. - */ - public void setAscending(boolean ascending) { - this.ascending = ascending; - } - - } - - private void parse(String orderByClause) { - - if (orderByClause == null) { - return; - } - - String[] chunks = orderByClause.split(","); - for (int i = 0; i < chunks.length; i++) { - - String[] pairs = chunks[i].split(" "); - Property p = parseProperty(pairs); - if (p != null) { - list.add(p); - } - } - } - - private Property parseProperty(String[] pairs) { - if (pairs.length == 0) { - return null; - } - - ArrayList wordList = new ArrayList(pairs.length); - for (int i = 0; i < pairs.length; i++) { - if (!isEmptyString(pairs[i])) { - wordList.add(pairs[i]); - } - } - if (wordList.isEmpty()) { - return null; - } - if (wordList.size() == 1) { - return new Property(wordList.get(0), true); - } - if (wordList.size() == 2) { - boolean asc = isAscending(wordList.get(1)); - return new Property(wordList.get(0), asc); - } - String m = "Expecting a max of 2 words in [" + Arrays.toString(pairs) - + "] but got " + wordList.size(); - throw new RuntimeException(m); - } - - private boolean isAscending(String s) { - s = s.toLowerCase(); - if (s.startsWith("asc")) { - return true; - } - if (s.startsWith("desc")) { - return false; - } - String m = "Expecting [" + s + "] to be asc or desc?"; - throw new RuntimeException(m); - } - - private boolean isEmptyString(String s) { - return s == null || s.length() == 0; - } -} +package com.avaje.ebean; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Represents an Order By for a Query. + *

+ * Is a ordered list of OrderBy.Property objects each specifying a property and + * whether it is ascending or descending order. + *

+ *

+ * Typically you will not construct an OrderBy yourself but use one that exists + * on the Query object. + *

+ */ +public final class OrderBy implements Serializable { + + private static final long serialVersionUID = 9157089257745730539L; + + private transient Query query; + + private final List list; + + /** + * Create an empty OrderBy with no associated query. + */ + public OrderBy() { + this.list = new ArrayList(2); + } + + private OrderBy(List list) { + this.list = list; + } + + /** + * Create an orderBy parsing the order by clause. + *

+ * The order by clause follows SQL order by clause with comma's between each + * property and optionally "asc" or "desc" to represent ascending or + * descending order respectively. + *

+ */ + public OrderBy(String orderByClause) { + this(null, orderByClause); + } + + /** + * Construct with a given query and order by clause. + */ + public OrderBy(Query query, String orderByClause) { + this.query = query; + this.list = new ArrayList(2); + parse(orderByClause); + } + + /** + * Reverse the ascending/descending order on all the properties. + */ + public void reverse() { + for (int i = 0; i < list.size(); i++) { + list.get(i).reverse(); + } + } + + /** + * Add a property with ascending order to this OrderBy. + */ + public Query asc(String propertyName) { + + list.add(new Property(propertyName, true)); + return query; + } + + /** + * Add a property with descending order to this OrderBy. + */ + public Query desc(String propertyName) { + + list.add(new Property(propertyName, false)); + return query; + } + + /** + * Return a copy of this OrderBy with the path trimmed. + */ + public OrderBy copyWithTrim(String path) { + List newList = new ArrayList(list.size()); + for (int i = 0; i < list.size(); i++) { + newList.add(list.get(i).copyWithTrim(path)); + } + return new OrderBy(newList); + } + + /** + * Return the properties for this OrderBy. + */ + public List getProperties() { + // not returning an Immutable list at this point + return list; + } + + /** + * Return true if this OrderBy does not have any properties. + */ + public boolean isEmpty() { + return list.isEmpty(); + } + + /** + * Return the associated query if there is one. + */ + public Query getQuery() { + return query; + } + + /** + * Associate this OrderBy with a query. + */ + public void setQuery(Query query) { + this.query = query; + } + + /** + * Return a copy of the OrderBy. + */ + public OrderBy copy() { + + OrderBy copy = new OrderBy(); + for (int i = 0; i < list.size(); i++) { + copy.add(list.get(i).copy()); + } + return copy; + } + + /** + * Add a property to the order by. + */ + public void add(Property p) { + list.add(p); + } + + public String toString() { + return list.toString(); + } + + /** + * Returns the OrderBy in string format. + */ + public String toStringFormat() { + if (list.isEmpty()) { + return null; + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < list.size(); i++) { + Property property = list.get(i); + if (i > 0) { + sb.append(", "); + } + sb.append(property.toStringFormat()); + } + return sb.toString(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof OrderBy)) { + return false; + } + + OrderBy e = (OrderBy) obj; + return e.list.equals(list); + } + + /** + * Return a hash value for this OrderBy. This can be to determine logical + * equality for OrderBy clauses. + */ + public int hashCode() { + return list.hashCode(); + } + + /** + * A property and its ascending descending order. + */ + public static final class Property implements Serializable { + + private static final long serialVersionUID = 1546009780322478077L; + + private String property; + + private boolean ascending; + + public Property(String property, boolean ascending) { + this.property = property; + this.ascending = ascending; + } + + /** + * Return a copy of this Property with the path trimmed. + */ + public Property copyWithTrim(String path) { + return new Property(property.substring(path.length() + 1), ascending); + } + + public int hashCode() { + int hc = property.hashCode(); + hc = hc * 31 + (ascending ? 0 : 1); + return hc; + } + + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Property)) { + return false; + } + + Property e = (Property) obj; + return e.ascending == ascending + && e.property.equals(property); + } + + public String toString() { + return toStringFormat(); + } + + public String toStringFormat() { + if (ascending) { + return property; + } else { + return property + " desc"; + } + } + + /** + * Reverse the ascending/descending order for this property. + */ + public void reverse() { + this.ascending = !ascending; + } + + /** + * Trim off the pathPrefix. + */ + public void trim(String pathPrefix) { + property = property.substring(pathPrefix.length() + 1); + } + + /** + * Return a copy of this property. + */ + public Property copy() { + return new Property(property, ascending); + } + + /** + * Return the property name. + */ + public String getProperty() { + return property; + } + + /** + * Set the property name. + */ + public void setProperty(String property) { + this.property = property; + } + + /** + * Return true if the order is ascending. + */ + public boolean isAscending() { + return ascending; + } + + /** + * Set to true if the order is ascending. + */ + public void setAscending(boolean ascending) { + this.ascending = ascending; + } + + } + + private void parse(String orderByClause) { + + if (orderByClause == null) { + return; + } + + String[] chunks = orderByClause.split(","); + for (int i = 0; i < chunks.length; i++) { + + String[] pairs = chunks[i].split(" "); + Property p = parseProperty(pairs); + if (p != null) { + list.add(p); + } + } + } + + private Property parseProperty(String[] pairs) { + if (pairs.length == 0) { + return null; + } + + ArrayList wordList = new ArrayList(pairs.length); + for (int i = 0; i < pairs.length; i++) { + if (!isEmptyString(pairs[i])) { + wordList.add(pairs[i]); + } + } + if (wordList.isEmpty()) { + return null; + } + if (wordList.size() == 1) { + return new Property(wordList.get(0), true); + } + if (wordList.size() == 2) { + boolean asc = isAscending(wordList.get(1)); + return new Property(wordList.get(0), asc); + } + String m = "Expecting a max of 2 words in [" + Arrays.toString(pairs) + + "] but got " + wordList.size(); + throw new RuntimeException(m); + } + + private boolean isAscending(String s) { + s = s.toLowerCase(); + if (s.startsWith("asc")) { + return true; + } + if (s.startsWith("desc")) { + return false; + } + String m = "Expecting [" + s + "] to be asc or desc?"; + throw new RuntimeException(m); + } + + private boolean isEmptyString(String s) { + return s == null || s.length() == 0; + } +} diff --git a/src/main/java/com/avaje/ebean/PagedList.java b/src/main/java/com/avaje/ebean/PagedList.java index e9e096c40..c622769ed 100644 --- a/src/main/java/com/avaje/ebean/PagedList.java +++ b/src/main/java/com/avaje/ebean/PagedList.java @@ -1,192 +1,192 @@ -package com.avaje.ebean; - -import java.util.List; -import java.util.concurrent.Future; - -/** - * Represents a page of results. - *

- * The benefit of using PagedList over just using the normal Query with - * {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} is that it additionally wraps - * functionality that can call {@link Query#findFutureRowCount()} to determine total row count, - * total page count etc. - *

- *

- * Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on - * 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();
- *
- * }
- * - *

Example: No total row count required

- *
{@code
- *
- *     // If you are not getting the 'first page' often
- *     // you do not bother getting the total row count again
- *     // so instead just get the page list of data
- *
- *     // fetch and return the list in the foreground thread
- *     List orders = pagedList.getList();
- *
- * }
- * - * @param - * the entity bean type - * - * @see Query#findPagedList(int, int) - */ -public interface PagedList { - - /** - * Initiate the loading of the total row count in the background. - *
{@code
-   *
-   *     // 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();
-   *
-   * }
- * - *

- * Also note that using loadRowCount() and getTotalRowCount() rather than getFutureRowCount() - * means that exceptions ExecutionException, InterruptedException, TimeoutException are instead - * wrapped in the unchecked PersistenceException (which might be preferrable). - *

- */ - public void loadRowCount(); - - /** - * Return the Future row count. You might get this if you wish to cancel the total row count query - * or specify a timeout for the row count query. - *

- * The loadRowCount() & getTotalRowCount() methods internally make use of this getFutureRowCount() method. - * Generally I expect people to prefer loadRowCount() & getTotalRowCount() over getFutureRowCount(). - *

- *
{@code
-   *
-   *     // initiate the row count query in the background thread
-   *     Future rowCount = pagedList.getFutureRowCount();
-   *
-   *     // fetch and return the list in the foreground thread
-   *     List orders = pagedList.getList();
-   *
-   *     // now get the total count with a timeout
-   *     Integer totalRowCount = rowCount.get(30, TimeUnit.SECONDS);
-   *
-   *     // or ge the total count without a timeout
-   *     Integer totalRowCountViaFuture = rowCount.get();
-   *
-   *     // which is actually the same as ...
-   *     int totalRowCount = pagedList.getTotalRowCount();
-   *
-   * }
- */ - public Future getFutureRowCount(); - - /** - * Return the list of entities for this page. - */ - public List getList(); - - /** - * Return the total row count for all pages. - *

- * If loadRowCount() has already been called then the row count query is already executing in a background thread - * and this gets the associated Future and gets the value waiting for the future to finish. - *

- *

- * If loadRowCount() has not been called then this executes the find row count query and returns the result and this - * will just occur in the current thread and not use a background thread. - *

- *
{@code
-   *
-   *     // 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 (which was being executed
-   *     // in a background thread if loadRowCount() was used)
-   *     int totalRowCount = pagedList.getTotalRowCount();
-   *
-   * }
- */ - public int getTotalRowCount(); - - /** - * Return the total number of pages based on the page size and total row count. - *

- * This method requires that the total row count has been fetched and will invoke - * the total row count query if it has not already been invoked. - *

- */ - public int getTotalPageCount(); - - /** - * Return the index position of this page. Zero based. - */ - public int getPageIndex(); - - /** - * Return true if there is a next page. - *

- * This method requires that the total row count has been fetched and will invoke - * the total row count query if it has not already been invoked. - *

- */ - public boolean hasNext(); - - /** - * Return true if there is a previous page. - */ - public boolean hasPrev(); - - /** - * 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. - *

- * This method requires that the total row count has been fetched and will invoke - * the total row count query if it has not already been invoked. - *

- * - * @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); -} +package com.avaje.ebean; + +import java.util.List; +import java.util.concurrent.Future; + +/** + * Represents a page of results. + *

+ * The benefit of using PagedList over just using the normal Query with + * {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} is that it additionally wraps + * functionality that can call {@link Query#findFutureRowCount()} to determine total row count, + * total page count etc. + *

+ *

+ * Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on + * 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();
+ *
+ * }
+ * + *

Example: No total row count required

+ *
{@code
+ *
+ *     // If you are not getting the 'first page' often
+ *     // you do not bother getting the total row count again
+ *     // so instead just get the page list of data
+ *
+ *     // fetch and return the list in the foreground thread
+ *     List orders = pagedList.getList();
+ *
+ * }
+ * + * @param + * the entity bean type + * + * @see Query#findPagedList(int, int) + */ +public interface PagedList { + + /** + * Initiate the loading of the total row count in the background. + *
{@code
+   *
+   *     // 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();
+   *
+   * }
+ * + *

+ * Also note that using loadRowCount() and getTotalRowCount() rather than getFutureRowCount() + * means that exceptions ExecutionException, InterruptedException, TimeoutException are instead + * wrapped in the unchecked PersistenceException (which might be preferrable). + *

+ */ + void loadRowCount(); + + /** + * Return the Future row count. You might get this if you wish to cancel the total row count query + * or specify a timeout for the row count query. + *

+ * The loadRowCount() & getTotalRowCount() methods internally make use of this getFutureRowCount() method. + * Generally I expect people to prefer loadRowCount() & getTotalRowCount() over getFutureRowCount(). + *

+ *
{@code
+   *
+   *     // initiate the row count query in the background thread
+   *     Future rowCount = pagedList.getFutureRowCount();
+   *
+   *     // fetch and return the list in the foreground thread
+   *     List orders = pagedList.getList();
+   *
+   *     // now get the total count with a timeout
+   *     Integer totalRowCount = rowCount.get(30, TimeUnit.SECONDS);
+   *
+   *     // or ge the total count without a timeout
+   *     Integer totalRowCountViaFuture = rowCount.get();
+   *
+   *     // which is actually the same as ...
+   *     int totalRowCount = pagedList.getTotalRowCount();
+   *
+   * }
+ */ + Future getFutureRowCount(); + + /** + * Return the list of entities for this page. + */ + List getList(); + + /** + * Return the total row count for all pages. + *

+ * If loadRowCount() has already been called then the row count query is already executing in a background thread + * and this gets the associated Future and gets the value waiting for the future to finish. + *

+ *

+ * If loadRowCount() has not been called then this executes the find row count query and returns the result and this + * will just occur in the current thread and not use a background thread. + *

+ *
{@code
+   *
+   *     // 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 (which was being executed
+   *     // in a background thread if loadRowCount() was used)
+   *     int totalRowCount = pagedList.getTotalRowCount();
+   *
+   * }
+ */ + int getTotalRowCount(); + + /** + * Return the total number of pages based on the page size and total row count. + *

+ * This method requires that the total row count has been fetched and will invoke + * the total row count query if it has not already been invoked. + *

+ */ + int getTotalPageCount(); + + /** + * Return the index position of this page. Zero based. + */ + int getPageIndex(); + + /** + * Return true if there is a next page. + *

+ * This method requires that the total row count has been fetched and will invoke + * the total row count query if it has not already been invoked. + *

+ */ + boolean hasNext(); + + /** + * Return true if there is a previous page. + */ + boolean hasPrev(); + + /** + * 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. + *

+ * This method requires that the total row count has been fetched and will invoke + * the total row count query if it has not already been invoked. + *

+ * + * @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. + */ + 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 9315cf790..9ee0eb428 100644 --- a/src/main/java/com/avaje/ebean/Query.java +++ b/src/main/java/com/avaje/ebean/Query.java @@ -1,1238 +1,1238 @@ -package com.avaje.ebean; - -import com.avaje.ebean.text.PathProperties; - -import java.io.Serializable; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Object relational query for finding a List, Set, Map or single entity bean. - *

- * Example: Create the query using the API. - *

- * - *
{@code
- *
- * List orderList = 
- *   ebeanServer.find(Order.class)
- *     .fetch("customer")
- *     .fetch("details")
- *     .where()
- *       .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 ";
- *   
- * 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();
- * ...
- * }
- * - *

Autofetch

- *

- * Ebean has built in support for "Autofetch". This is a mechanism where a query - * can be automatically tuned based on profiling information that is collected. - *

- *

- * This is effectively the same as automatically using select() and fetch() to - * build a query that will fetch all the data required by the application and no - * more. - *

- *

- * It is expected that Autofetch will be the default approach for many queries - * in a system. It is possibly not as useful where the result of a query is sent - * to a remote client or where there is some requirement for "Read Consistency" - * guarantees. - *

- * - *

Query Language

- *

- * Partial Objects - *

- *

- * The find and fetch clauses support specifying a list of - * properties to fetch. This results in objects that are "partially populated". - * If you try to get a property that was not populated a "lazy loading" query - * will automatically fire and load the rest of the properties of the bean (This - * is very similar behaviour as a reference object being "lazy loaded"). - *

- *

- * Partial objects can be saved just like fully populated objects. If you do - * this you should remember to include the "Version" property in the - * initial fetch. If you do not include a version property then optimistic - * 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} ) ] ]
- * [ where {predicates} ]
- * [ order by {order by properties} ]
- * [ limit {max rows} [ offset {first row} ] ]
- * }
- * - *

- * FIND {bean type} [ ( * | {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. - *

- *

- * 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} ) ] - *

- *

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

- *

- * For fetch of a path we can optionally specify a list of properties to fetch. - * If you do not specify a list of properties ALL the properties for that bean - * type are fetched. - *

- *

- * WHERE {list of predicates} - *

- *

- * The list of predicates which are joined by AND OR NOT ( and ). They can - * include named (or positioned) bind parameters. These parameters will need to - * be bound by {@link Query#setParameter(String, Object)}. - *

- *

- * ORDER BY {order by properties} - *

- *

- * The list of properties to order the result. You can include ASC (ascending) - * and DESC (descending) in the order by clause. - *

- *

- * LIMIT {max rows} [ OFFSET {first row} ] - *

- *

- * The limit offset specifies the max rows and first row to fetch. The offset is - * optional. - *

- *

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)
- * }
- * - *

- * 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 - * "Partial Objects" with only some of their properties populated. The customer - * objects will have their id, name and shipping address populated. The product - * 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. - */ -public interface Query extends Serializable { - - /** - * Return the RawSql that was set to use for this query. - */ - public RawSql getRawSql(); - - /** - * Set RawSql to use for this query. - */ - public Query setRawSql(RawSql rawSql); - - /** - * Cancel the query execution if supported by the underlying database and - * driver. - *

    - * This must be called from a different thread to the query executor. - *

    - */ - public void cancel(); - - /** - * Return a copy of the query. - *

    - * This is so that you can use a Query as a "prototype" for creating other - * query instances. You could create a Query with various where expressions - * and use that as a "prototype" - using this copy() method to create a new - * instance that you can then add other expressions then execute. - *

    - */ - public Query copy(); - - /** - * 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}. - *

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

    - * Note that #findEach uses a 'per object graph' PersistenceContext so this scope is ignored for - * queries executed as #findIterate, #findEach, #findEachWhile. - * - * @param scope The scope to use for this query and subsequent lazy loading. - */ - public Query setPersistenceContextScope(PersistenceContextScope scope); - - /** - * Return the ExpressionFactory used by this query. - */ - public ExpressionFactory getExpressionFactory(); - - /** - * Returns true if this query was tuned by autoFetch. - */ - public boolean isAutofetchTuned(); - - /** - * Explicitly specify whether to use Autofetch for this query. - *

    - * If you do not call this method on a query the "Implicit Autofetch mode" is - * used to determine if Autofetch should be used for a given query. - *

    - *

    - * Autofetch can add additional fetch paths to the query and specify which - * properties are included for each path. If you have explicitly defined some - * fetch paths Autofetch will not remove. - *

    - */ - public Query setAutofetch(boolean autofetch); - - /** - * Set the default lazy loading batch size to use. - *

    - * When lazy loading is invoked on beans loaded by this query then this sets the - * batch size used to load those beans. - * - * @param lazyLoadBatchSize the number of beans to lazy load in a single batch - */ - public Query setLazyLoadBatchSize(int lazyLoadBatchSize); - - /** - * 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. - *

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

    - * - *
    {@code
    -   *
    -   * 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();
    -   *
    -   * }
    - * - * @param fetchProperties - * the properties to fetch for this bean (* = all properties). - */ - public Query select(String fetchProperties); - - /** - * Specify a path to fetch with its specific properties to include - * (aka partial object). - *

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

    - * - *
    {@code
    -   *
    -   * // query orders...
    -   * List orders =
    -   *     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. - *

    - * - *
    {@code
    -   *
    -   * // fetch customers (their id, name and status)
    -   * 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. - * @param fetchProperties - * properties of the associated bean that you want to include in the - * fetch (* means all properties, null also means all properties). - */ - public Query fetch(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)
    -   * 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); - - /** - * Specify a path to load including all its properties. - *

    - * 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. - */ - public Query fetch(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)
    -   * 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); - - /** - * Apply the path properties replacing the select and fetch clauses. - *

    - * This is typically used when the PathProperties is applied to both the query and the JSON output. - *

    - */ - public Query apply(PathProperties pathProperties); - - /** - * Execute the query returning the list of Id's. - *

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

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

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

    - */ - public QueryIterator findIterate(); - - /** - * This is deprecated in favor of #findEachWhile. - *

    - * This is functionally exactly the same as #findEachWhile. It is - * replaced by findEachWhile because the method name is much better. - *

    - * - * @param visitor - * the visitor used to process the queried beans. - * - * @deprecated - */ - public void findVisit(QueryResultVisitor visitor); - - /** - * Execute the query processing the beans one at a time. - *

    - * This method is appropriate to process very large query results as the - * beans are consumed one at a time and do not need to be held in memory - * (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. - *

    - *

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

    - * - *
    {@code
    -   *
    -   *  ebeanServer.find(Customer.class)
    -   *     .where().eq("status", Status.NEW)
    -   *     .order().asc("id")
    -   *     .findEach((Customer customer) -> {
    -   *
    -   *       // do something with customer
    -   *       System.out.println("-- visit " + customer);
    -   *     });
    -   *
    -   * }
    - * - * @param consumer - * the consumer used to process the queried beans. - */ - public void findEach(QueryEachConsumer consumer); - - /** - * Execute the query using callbacks to a visitor to process the resulting - * beans one at a time. - *

    - * 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)
    -   *     .fetch("contacts", new FetchConfig().query(2))
    -   *     .where().eq("status", Status.NEW)
    -   *     .order().asc("id")
    -   *     .setMaxRows(2000)
    -   *     .findEachWhile((Customer customer) -> {
    -   *
    -   *       // do something with customer
    -   *       System.out.println("-- visit " + customer);
    -   *
    -   *       // return true to continue processing or false to stop
    -   *       return (customer.getId() < 40);
    -   *     });
    -   *
    -   * }
    - * - * @param consumer - * the consumer used to process the queried beans. - */ - public void findEachWhile(QueryEachWhileConsumer consumer); - - /** - * Execute the query returning the list of objects. - *

    - * 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(); - - /** - * Execute the query returning the set of objects. - *

    - * 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(); - - /** - * Execute the query returning a map of the objects. - *

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

    - *

    - * 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 =
    -   *   ebeanServer.find(Product.class)
    -   *     .setMapKey("sku")
    -   *     .findMap();
    -   *
    -   * }
    - * - * @see EbeanServer#findMap(Query, Transaction) - */ - public Map findMap(); - - /** - * Return a typed map specifying the key property and type. - */ - public Map findMap(String keyProperty, Class keyType); - - /** - * Execute the query returning either a single bean or null (if no matching - * bean is found). - *

    - * If more than 1 row is found for this query then a PersistenceException is - * thrown. - *

    - *

    - * 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...
    -   * Product product =
    -   *     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 = 
    -   *     ebeanServer.find(Order.class)
    -   *       .setId(1)
    -   *       .fetch("details")
    -   *       .findUnique();
    -   *
    -   * // the order details were eagerly loaded
    -   * List details = order.getDetails();
    -   * ...
    -   * }
    - */ - public T findUnique(); - - /** - * Return the count of entities this query should return. - *

    - * This is the number of 'top level' or 'root level' entities. - *

    - */ - public int findRowCount(); - - /** - * 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 - */ - public FutureRowCount findFutureRowCount(); - - /** - * Execute find Id's 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 list of Id's - */ - public FutureIds findFutureIds(); - - /** - * Execute find list query in a background thread. - *

    - * This query will execute in it's own PersistenceContext and using its own transaction. - * What that means is that it will not share any bean instances with other queries. - *

    - * - * @return a Future object for the list result of the query - */ - public FutureList findFutureList(); - - /** - * Return a PagedList for this query. - *

    - * The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and - * {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to - * {@link Query#findFutureRowCount()} to determine total row count, total page count etc. - *

    - *

    - * Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on - * 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 - * The number of beans to return per page. - * @return The PagedList - */ - public PagedList findPagedList(int pageIndex, int pageSize); - - /** - * 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 - */ - public Query setParameter(String name, Object value); - - /** - * 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. - */ - public Query setParameter(int position, Object value); - - /** - * Set the Id value to query. This is used with findUnique(). - *

    - * You can use this to have further control over the query. For example adding - * fetch joins. - *

    - * - *
    {@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); - - /** - * Add additional clause(s) to the where clause. - *

    - * This typically contains named parameters which will need to be set via - * {@link #setParameter(String, Object)}. - *

    - * - *
    {@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%");
    -   * }
    -   *
    -   * }
    - * - *

    - * Internally the addToWhereClause string is processed by removing named - * parameters (replacing them with ?) and by converting logical property names - * to database column names (with table alias). The rest of the string is left - * as is and it is completely acceptable and expected for the addToWhereClause - * string to include sql functions and columns. - *

    - * - * @param addToWhereClause - * the clause to append to the where clause which typically contains - * named parameters. - * @return The query object - */ - public Query where(String addToWhereClause); - - /** - * Add a single Expression to the where clause returning the query. - * - *
    {@code
    -   *
    -   * List newOrders = 
    -   *     ebeanServer.find(Order.class)
    -   * 		.where().eq("status", Order.NEW)
    -   * 		.findList();
    -   * ...
    -   *
    -   * }
    - */ - public Query where(Expression expression); - - /** - * 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 =
    -   *     ebeanServer.find(Order.class)
    -   *     .where()
    -   *       .eq("status", Order.NEW)
    -   *       .ilike("customer.name","rob%")
    -   *     .findList();
    -   *
    -   * }
    - * - * @see Expr - * @return The ExpressionList for adding expressions to. - */ - public ExpressionList where(); - - /** - * This applies a filter on the 'many' property list rather than the root - * level objects. - *

    - * Typically you will use this in a scenario where the cardinality is high on - * the 'many' property you wish to join to. Say you want to fetch customers - * and their associated orders... but instead of getting all the orders for - * 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())
    -   *     // .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 - * 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. - * - * @return the expression list that you add filter expressions for the many - * to. - */ - public ExpressionList filterMany(String propertyName); - - /** - * Add Expressions to the Having clause return the ExpressionList. - *

    - * Currently only beans based on raw sql will use the having clause. - *

    - *

    - * 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. - */ - public ExpressionList having(); - - /** - * Add additional clause(s) to the having clause. - *

    - * This typically contains named parameters which will need to be set via - * {@link #setParameter(String, Object)}. - *

    - * - *
    {@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 - * named parameters. - * @return The query object - */ - public Query having(String addToHavingClause); - - /** - * Add an expression to the having clause returning the query. - *

    - * Currently only beans based on raw sql will use the having clause. - *

    - *

    - * This is similar to {@link #having()} except it returns the query rather - * 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. - * @return the Query object - */ - public Query having(Expression addExpressionToHaving); - - /** - * Set the order by clause replacing the existing order by clause if there is - * one. - *

    - * This follows SQL syntax using commas between each property with the - * optional asc and desc keywords representing ascending and descending order - * respectively. - *

    - *

    - * This is EXACTLY the same as {@link #order(String)}. - *

    - */ - public Query orderBy(String orderByClause); - - /** - * Set the order by clause replacing the existing order by clause if there is - * one. - *

    - * This follows SQL syntax using commas between each property with the - * optional asc and desc keywords representing ascending and descending order - * respectively. - *

    - *

    - * This is EXACTLY the same as {@link #orderBy(String)}. - *

    - */ - public Query order(String orderByClause); - - /** - * Return the OrderBy so that you can append an ascending or descending - * property to the order by clause. - *

    - * This will never return a null. If no order by clause exists then an 'empty' - * OrderBy object is returned. - *

    - *

    - * This is EXACTLY the same as {@link #orderBy()}. - *

    - */ - public OrderBy order(); - - /** - * Return the OrderBy so that you can append an ascending or descending - * property to the order by clause. - *

    - * This will never return a null. If no order by clause exists then an 'empty' - * OrderBy object is returned. - *

    - *

    - * This is EXACTLY the same as {@link #order()}. - *

    - */ - public OrderBy orderBy(); - - /** - * Set an OrderBy object to replace any existing OrderBy clause. - *

    - * This is EXACTLY the same as {@link #setOrderBy(OrderBy)}. - *

    - */ - public Query setOrder(OrderBy orderBy); - - /** - * Set an OrderBy object to replace any existing OrderBy clause. - *

    - * This is EXACTLY the same as {@link #setOrder(OrderBy)}. - *

    - */ - public Query setOrderBy(OrderBy orderBy); - - /** - * Set whether this query uses DISTINCT. - */ - public Query setDistinct(boolean isDistinct); - - /** - * Return the first row value. - */ - public int getFirstRow(); - - /** - * Set the first row to return for this query. - * - * @param firstRow - */ - public Query setFirstRow(int firstRow); - - /** - * Return the max rows for this query. - */ - public int getMaxRows(); - - /** - * Set the maximum number of rows to return in the query. - * - * @param maxRows - * the maximum number of rows to return in the query. - */ - public Query setMaxRows(int maxRows); - - /** - * Set the property to use as keys for a map. - *

    - * 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...
    -   *     .setMapKey("sku")
    -   *     .findMap();
    -   *
    -   * }
    - * - * @param mapKey - * the property to use as keys for a map. - */ - public Query setMapKey(String mapKey); - - /** - * Set this to true to use the bean cache. - *

    - * If the query result is in cache then by default this same instance is - * returned. In this sense it should be treated as a read only object graph. - *

    - */ - public Query setUseCache(boolean useBeanCache); - - /** - * Set this to true to use the query cache. - */ - public Query setUseQueryCache(boolean useQueryCache); - - /** - * When set to true when you want the returned beans to be read only. - */ - public Query setReadOnly(boolean readOnly); - - /** - * When set to true all the beans from this query are loaded into the bean - * cache. - */ - public Query setLoadBeanCache(boolean loadBeanCache); - - /** - * Set a timeout on this query. - *

    - * This will typically result in a call to setQueryTimeout() on a - * 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. - */ - public Query setTimeout(int secs); - - /** - * A hint which for JDBC translates to the Statement.fetchSize(). - *

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

    - */ - public Query setBufferFetchSizeHint(int fetchSize); - - /** - * Return the sql that was generated for executing this query. - *

    - * This is only available after the query has been executed and provided only - * for informational purposes. - *

    - */ - public String getGeneratedSql(); - - /** - * executed the select with "for update" which should lock the record - * "on read" - */ - public Query setForUpdate(boolean forUpdate); - - /** - * Return true if this query has forUpdate set. - */ - public boolean isForUpdate(); - - /** - * Set root table alias. - */ - public Query alias(String alias); -} +package com.avaje.ebean; + +import com.avaje.ebean.text.PathProperties; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Object relational query for finding a List, Set, Map or single entity bean. + *

    + * Example: Create the query using the API. + *

    + * + *
    {@code
    + *
    + * List orderList = 
    + *   ebeanServer.find(Order.class)
    + *     .fetch("customer")
    + *     .fetch("details")
    + *     .where()
    + *       .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 ";
    + *   
    + * 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();
    + * ...
    + * }
    + * + *

    Autofetch

    + *

    + * Ebean has built in support for "Autofetch". This is a mechanism where a query + * can be automatically tuned based on profiling information that is collected. + *

    + *

    + * This is effectively the same as automatically using select() and fetch() to + * build a query that will fetch all the data required by the application and no + * more. + *

    + *

    + * It is expected that Autofetch will be the default approach for many queries + * in a system. It is possibly not as useful where the result of a query is sent + * to a remote client or where there is some requirement for "Read Consistency" + * guarantees. + *

    + * + *

    Query Language

    + *

    + * Partial Objects + *

    + *

    + * The find and fetch clauses support specifying a list of + * properties to fetch. This results in objects that are "partially populated". + * If you try to get a property that was not populated a "lazy loading" query + * will automatically fire and load the rest of the properties of the bean (This + * is very similar behaviour as a reference object being "lazy loaded"). + *

    + *

    + * Partial objects can be saved just like fully populated objects. If you do + * this you should remember to include the "Version" property in the + * initial fetch. If you do not include a version property then optimistic + * 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} ) ] ]
    + * [ where {predicates} ]
    + * [ order by {order by properties} ]
    + * [ limit {max rows} [ offset {first row} ] ]
    + * }
    + * + *

    + * FIND {bean type} [ ( * | {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. + *

    + *

    + * 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} ) ] + *

    + *

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

    + *

    + * For fetch of a path we can optionally specify a list of properties to fetch. + * If you do not specify a list of properties ALL the properties for that bean + * type are fetched. + *

    + *

    + * WHERE {list of predicates} + *

    + *

    + * The list of predicates which are joined by AND OR NOT ( and ). They can + * include named (or positioned) bind parameters. These parameters will need to + * be bound by {@link Query#setParameter(String, Object)}. + *

    + *

    + * ORDER BY {order by properties} + *

    + *

    + * The list of properties to order the result. You can include ASC (ascending) + * and DESC (descending) in the order by clause. + *

    + *

    + * LIMIT {max rows} [ OFFSET {first row} ] + *

    + *

    + * The limit offset specifies the max rows and first row to fetch. The offset is + * optional. + *

    + *

    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)
    + * }
    + * + *

    + * 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 + * "Partial Objects" with only some of their properties populated. The customer + * objects will have their id, name and shipping address populated. The product + * 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. + */ +public interface Query extends Serializable { + + /** + * Return the RawSql that was set to use for this query. + */ + RawSql getRawSql(); + + /** + * Set RawSql to use for this query. + */ + Query setRawSql(RawSql rawSql); + + /** + * Cancel the query execution if supported by the underlying database and + * driver. + *

    + * This must be called from a different thread to the query executor. + *

    + */ + void cancel(); + + /** + * Return a copy of the query. + *

    + * This is so that you can use a Query as a "prototype" for creating other + * query instances. You could create a Query with various where expressions + * and use that as a "prototype" - using this copy() method to create a new + * instance that you can then add other expressions then execute. + *

    + */ + Query copy(); + + /** + * 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}. + *

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

    + * Note that #findEach uses a 'per object graph' PersistenceContext so this scope is ignored for + * queries executed as #findIterate, #findEach, #findEachWhile. + * + * @param scope The scope to use for this query and subsequent lazy loading. + */ + Query setPersistenceContextScope(PersistenceContextScope scope); + + /** + * Return the ExpressionFactory used by this query. + */ + ExpressionFactory getExpressionFactory(); + + /** + * Returns true if this query was tuned by autoFetch. + */ + boolean isAutofetchTuned(); + + /** + * Explicitly specify whether to use Autofetch for this query. + *

    + * If you do not call this method on a query the "Implicit Autofetch mode" is + * used to determine if Autofetch should be used for a given query. + *

    + *

    + * Autofetch can add additional fetch paths to the query and specify which + * properties are included for each path. If you have explicitly defined some + * fetch paths Autofetch will not remove. + *

    + */ + Query setAutofetch(boolean autofetch); + + /** + * Set the default lazy loading batch size to use. + *

    + * When lazy loading is invoked on beans loaded by this query then this sets the + * batch size used to load those beans. + * + * @param lazyLoadBatchSize the number of beans to lazy load in a single batch + */ + Query setLazyLoadBatchSize(int lazyLoadBatchSize); + + /** + * 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. + *

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

    + * + *
    {@code
    +   *
    +   * 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();
    +   *
    +   * }
    + * + * @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). + *

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

    + * + *
    {@code
    +   *
    +   * // query orders...
    +   * List orders =
    +   *     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. + *

    + * + *
    {@code
    +   *
    +   * // fetch customers (their id, name and status)
    +   * 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. + * @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); + + /** + * 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();
    +   *
    +   * }
    + */ + Query fetch(String assocProperty, String fetchProperties, FetchConfig fetchConfig); + + /** + * Specify a path to load including all its properties. + *

    + * 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. + */ + Query fetch(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)
    +   * List customers =
    +   *     ebeanServer.find(Customer.class)
    +   *     // lazy fetch contacts with a batch size of 100
    +   *     .fetch("contacts", new FetchConfig().lazy(100))
    +   *     .findList();
    +   *
    +   * }
    + */ + Query fetch(String path, FetchConfig joinConfig); + + /** + * Apply the path properties replacing the select and fetch clauses. + *

    + * This is typically used when the PathProperties is applied to both the query and the JSON output. + *

    + */ + Query apply(PathProperties pathProperties); + + /** + * Execute the query returning the list of Id's. + *

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

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

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

    + */ + QueryIterator findIterate(); + + /** + * This is deprecated in favor of #findEachWhile. + *

    + * This is functionally exactly the same as #findEachWhile. It is + * replaced by findEachWhile because the method name is much better. + *

    + * + * @param visitor + * the visitor used to process the queried beans. + * + * @deprecated + */ + void findVisit(QueryResultVisitor visitor); + + /** + * Execute the query processing the beans one at a time. + *

    + * This method is appropriate to process very large query results as the + * beans are consumed one at a time and do not need to be held in memory + * (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. + *

    + *

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

    + * + *
    {@code
    +   *
    +   *  ebeanServer.find(Customer.class)
    +   *     .where().eq("status", Status.NEW)
    +   *     .order().asc("id")
    +   *     .findEach((Customer customer) -> {
    +   *
    +   *       // do something with customer
    +   *       System.out.println("-- visit " + customer);
    +   *     });
    +   *
    +   * }
    + * + * @param consumer + * the consumer used to process the queried beans. + */ + void findEach(QueryEachConsumer consumer); + + /** + * Execute the query using callbacks to a visitor to process the resulting + * beans one at a time. + *

    + * 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)
    +   *     .fetch("contacts", new FetchConfig().query(2))
    +   *     .where().eq("status", Status.NEW)
    +   *     .order().asc("id")
    +   *     .setMaxRows(2000)
    +   *     .findEachWhile((Customer customer) -> {
    +   *
    +   *       // do something with customer
    +   *       System.out.println("-- visit " + customer);
    +   *
    +   *       // return true to continue processing or false to stop
    +   *       return (customer.getId() < 40);
    +   *     });
    +   *
    +   * }
    + * + * @param consumer + * the consumer used to process the queried beans. + */ + void findEachWhile(QueryEachWhileConsumer consumer); + + /** + * Execute the query returning the list of objects. + *

    + * 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) + */ + List findList(); + + /** + * Execute the query returning the set of objects. + *

    + * 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) + */ + Set findSet(); + + /** + * Execute the query returning a map of the objects. + *

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

    + *

    + * 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 =
    +   *   ebeanServer.find(Product.class)
    +   *     .setMapKey("sku")
    +   *     .findMap();
    +   *
    +   * }
    + * + * @see EbeanServer#findMap(Query, Transaction) + */ + Map findMap(); + + /** + * Return a typed map specifying the key property and type. + */ + Map findMap(String keyProperty, Class keyType); + + /** + * Execute the query returning either a single bean or null (if no matching + * bean is found). + *

    + * If more than 1 row is found for this query then a PersistenceException is + * thrown. + *

    + *

    + * 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...
    +   * Product product =
    +   *     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 = 
    +   *     ebeanServer.find(Order.class)
    +   *       .setId(1)
    +   *       .fetch("details")
    +   *       .findUnique();
    +   *
    +   * // the order details were eagerly loaded
    +   * List details = order.getDetails();
    +   * ...
    +   * }
    + */ + T findUnique(); + + /** + * Return the count of entities this query should return. + *

    + * This is the number of 'top level' or 'root level' entities. + *

    + */ + int findRowCount(); + + /** + * 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 + */ + FutureRowCount findFutureRowCount(); + + /** + * Execute find Id's 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 list of Id's + */ + FutureIds findFutureIds(); + + /** + * Execute find list query in a background thread. + *

    + * This query will execute in it's own PersistenceContext and using its own transaction. + * What that means is that it will not share any bean instances with other queries. + *

    + * + * @return a Future object for the list result of the query + */ + FutureList findFutureList(); + + /** + * Return a PagedList for this query. + *

    + * The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and + * {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to + * {@link Query#findFutureRowCount()} to determine total row count, total page count etc. + *

    + *

    + * Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on + * 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 + * The number of beans to return per page. + * @return The PagedList + */ + PagedList findPagedList(int pageIndex, int pageSize); + + /** + * 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 + */ + Query setParameter(String name, Object value); + + /** + * 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. + */ + Query setParameter(int position, Object value); + + /** + * Set the Id value to query. This is used with findUnique(). + *

    + * You can use this to have further control over the query. For example adding + * fetch joins. + *

    + * + *
    {@code
    +   *
    +   * Order order =
    +   *     ebeanServer.find(Order.class)
    +   *     .setId(1)
    +   *     .fetch("details")
    +   *     .findUnique();
    +   *
    +   * // the order details were eagerly fetched
    +   * List details = order.getDetails();
    +   *
    +   * }
    + */ + Query setId(Object id); + + /** + * Add additional clause(s) to the where clause. + *

    + * This typically contains named parameters which will need to be set via + * {@link #setParameter(String, Object)}. + *

    + * + *
    {@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%");
    +   * }
    +   *
    +   * }
    + * + *

    + * Internally the addToWhereClause string is processed by removing named + * parameters (replacing them with ?) and by converting logical property names + * to database column names (with table alias). The rest of the string is left + * as is and it is completely acceptable and expected for the addToWhereClause + * string to include sql functions and columns. + *

    + * + * @param addToWhereClause + * the clause to append to the where clause which typically contains + * named parameters. + * @return The query object + */ + Query where(String addToWhereClause); + + /** + * Add a single Expression to the where clause returning the query. + * + *
    {@code
    +   *
    +   * List newOrders = 
    +   *     ebeanServer.find(Order.class)
    +   * 		.where().eq("status", Order.NEW)
    +   * 		.findList();
    +   * ...
    +   *
    +   * }
    + */ + Query where(Expression expression); + + /** + * 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 =
    +   *     ebeanServer.find(Order.class)
    +   *     .where()
    +   *       .eq("status", Order.NEW)
    +   *       .ilike("customer.name","rob%")
    +   *     .findList();
    +   *
    +   * }
    + * + * @see Expr + * @return The ExpressionList for adding expressions to. + */ + ExpressionList where(); + + /** + * This applies a filter on the 'many' property list rather than the root + * level objects. + *

    + * Typically you will use this in a scenario where the cardinality is high on + * the 'many' property you wish to join to. Say you want to fetch customers + * and their associated orders... but instead of getting all the orders for + * 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())
    +   *     // .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 + * 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. + * + * @return the expression list that you add filter expressions for the many + * to. + */ + ExpressionList filterMany(String propertyName); + + /** + * Add Expressions to the Having clause return the ExpressionList. + *

    + * Currently only beans based on raw sql will use the having clause. + *

    + *

    + * 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. + */ + ExpressionList having(); + + /** + * Add additional clause(s) to the having clause. + *

    + * This typically contains named parameters which will need to be set via + * {@link #setParameter(String, Object)}. + *

    + * + *
    {@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 + * named parameters. + * @return The query object + */ + Query having(String addToHavingClause); + + /** + * Add an expression to the having clause returning the query. + *

    + * Currently only beans based on raw sql will use the having clause. + *

    + *

    + * This is similar to {@link #having()} except it returns the query rather + * 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. + * @return the Query object + */ + Query having(Expression addExpressionToHaving); + + /** + * Set the order by clause replacing the existing order by clause if there is + * one. + *

    + * This follows SQL syntax using commas between each property with the + * optional asc and desc keywords representing ascending and descending order + * respectively. + *

    + *

    + * This is EXACTLY the same as {@link #order(String)}. + *

    + */ + Query orderBy(String orderByClause); + + /** + * Set the order by clause replacing the existing order by clause if there is + * one. + *

    + * This follows SQL syntax using commas between each property with the + * optional asc and desc keywords representing ascending and descending order + * respectively. + *

    + *

    + * This is EXACTLY the same as {@link #orderBy(String)}. + *

    + */ + Query order(String orderByClause); + + /** + * Return the OrderBy so that you can append an ascending or descending + * property to the order by clause. + *

    + * This will never return a null. If no order by clause exists then an 'empty' + * OrderBy object is returned. + *

    + *

    + * This is EXACTLY the same as {@link #orderBy()}. + *

    + */ + OrderBy order(); + + /** + * Return the OrderBy so that you can append an ascending or descending + * property to the order by clause. + *

    + * This will never return a null. If no order by clause exists then an 'empty' + * OrderBy object is returned. + *

    + *

    + * This is EXACTLY the same as {@link #order()}. + *

    + */ + OrderBy orderBy(); + + /** + * Set an OrderBy object to replace any existing OrderBy clause. + *

    + * This is EXACTLY the same as {@link #setOrderBy(OrderBy)}. + *

    + */ + Query setOrder(OrderBy orderBy); + + /** + * Set an OrderBy object to replace any existing OrderBy clause. + *

    + * This is EXACTLY the same as {@link #setOrder(OrderBy)}. + *

    + */ + Query setOrderBy(OrderBy orderBy); + + /** + * Set whether this query uses DISTINCT. + */ + Query setDistinct(boolean isDistinct); + + /** + * Return the first row value. + */ + int getFirstRow(); + + /** + * Set the first row to return for this query. + * + * @param firstRow + */ + Query setFirstRow(int firstRow); + + /** + * Return the max rows for this query. + */ + int getMaxRows(); + + /** + * Set 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); + + /** + * Set the property to use as keys for a map. + *

    + * 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...
    +   *     .setMapKey("sku")
    +   *     .findMap();
    +   *
    +   * }
    + * + * @param mapKey + * the property to use as keys for a map. + */ + Query setMapKey(String mapKey); + + /** + * Set this to true to use the bean cache. + *

    + * If the query result is in cache then by default this same instance is + * returned. In this sense it should be treated as a read only object graph. + *

    + */ + Query setUseCache(boolean useBeanCache); + + /** + * Set this to true to use the query cache. + */ + Query setUseQueryCache(boolean useQueryCache); + + /** + * When set to true when you want the returned beans to be read only. + */ + Query setReadOnly(boolean readOnly); + + /** + * When set to true all the beans from this query are loaded into the bean + * cache. + */ + Query setLoadBeanCache(boolean loadBeanCache); + + /** + * Set a timeout on this query. + *

    + * This will typically result in a call to setQueryTimeout() on a + * 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. + */ + Query setTimeout(int secs); + + /** + * A hint which for JDBC translates to the Statement.fetchSize(). + *

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

    + */ + Query setBufferFetchSizeHint(int fetchSize); + + /** + * Return the sql that was generated for executing this query. + *

    + * This is only available after the query has been executed and provided only + * for informational purposes. + *

    + */ + String getGeneratedSql(); + + /** + * executed the select with "for update" which should lock the record + * "on read" + */ + Query setForUpdate(boolean forUpdate); + + /** + * Return true if this query has forUpdate set. + */ + boolean isForUpdate(); + + /** + * Set root table alias. + */ + Query alias(String alias); +} diff --git a/src/main/java/com/avaje/ebean/QueryEachConsumer.java b/src/main/java/com/avaje/ebean/QueryEachConsumer.java index 3d015456c..f7de39953 100644 --- a/src/main/java/com/avaje/ebean/QueryEachConsumer.java +++ b/src/main/java/com/avaje/ebean/QueryEachConsumer.java @@ -1,41 +1,41 @@ -package com.avaje.ebean; - -/** - * Used to process a query result one bean at a time via a callback to this - * visitor. - *

    - * If you wish to stop further processing return false from the accept method. - *

    - *

    - * Unlike findList() and findSet() using a QueryResultVisitor does not require - * all the beans in the query result to be held in memory at once. This makes - * QueryResultVisitor useful for processing large queries. - *

    - * - *
    {@code
    - *
    - * Query query = server.find(Customer.class)
    - *     .where().eq("status", Status.NEW)
    - *     .order().asc("id");
    - *
    - * query.findEach((Customer customer) -> {
    - *
    - *     // do something with customer
    - *     System.out.println("-- visit " + customer);
    - * });
    - *
    - * }
    - * - * @param - * the type of entity bean being queried. - */ -public interface QueryEachConsumer { - - /** - * Process the bean. - * - * @param bean - * the entity bean to process - */ - public void accept(T bean); -} +package com.avaje.ebean; + +/** + * Used to process a query result one bean at a time via a callback to this + * visitor. + *

    + * If you wish to stop further processing return false from the accept method. + *

    + *

    + * Unlike findList() and findSet() using a QueryResultVisitor does not require + * all the beans in the query result to be held in memory at once. This makes + * QueryResultVisitor useful for processing large queries. + *

    + * + *
    {@code
    + *
    + * Query query = server.find(Customer.class)
    + *     .where().eq("status", Status.NEW)
    + *     .order().asc("id");
    + *
    + * query.findEach((Customer customer) -> {
    + *
    + *     // do something with customer
    + *     System.out.println("-- visit " + customer);
    + * });
    + *
    + * }
    + * + * @param + * the type of entity bean being queried. + */ +public interface QueryEachConsumer { + + /** + * Process the bean. + * + * @param bean + * the entity bean to process + */ + void accept(T bean); +} diff --git a/src/main/java/com/avaje/ebean/QueryEachWhileConsumer.java b/src/main/java/com/avaje/ebean/QueryEachWhileConsumer.java index 1b503099c..251fd4326 100644 --- a/src/main/java/com/avaje/ebean/QueryEachWhileConsumer.java +++ b/src/main/java/com/avaje/ebean/QueryEachWhileConsumer.java @@ -1,45 +1,45 @@ -package com.avaje.ebean; - -/** - * Used to process a query result one bean at a time via a callback to this - * visitor. - *

    - * If you wish to stop further processing return false from the accept method. - *

    - *

    - * Unlike findList() and findSet() using a QueryResultVisitor does not require - * all the beans in the query result to be held in memory at once. This makes - * QueryResultVisitor useful for processing large queries. - *

    - *

    - *

    - *
    - * Query<Customer> query = server.find(Customer.class)
    - *     .fetch("contacts", new FetchConfig().query(2))
    - *     .where().gt("id", 0)
    - *     .orderBy("id")
    - *     .setMaxRows(2);
    - *
    - * query.findEachWhile((Customer customer) -> {
    - *
    - *     // do something with customer
    - *     System.out.println("-- visit " + customer);
    - *
    - *     // return true to continue processing or false to stop
    - *     return (customer.getId() < 40);
    - * });
    - * 
    - * - * @param the type of entity bean being queried. - */ -public interface QueryEachWhileConsumer { - - /** - * Process the bean and return true if you want to continue processing more - * beans. Return false if you want to stop processing further. - * - * @param bean the entity bean to process - * @return true to continue processing more beans or false to stop. - */ - public boolean accept(T bean); -} +package com.avaje.ebean; + +/** + * Used to process a query result one bean at a time via a callback to this + * visitor. + *

    + * If you wish to stop further processing return false from the accept method. + *

    + *

    + * Unlike findList() and findSet() using a QueryResultVisitor does not require + * all the beans in the query result to be held in memory at once. This makes + * QueryResultVisitor useful for processing large queries. + *

    + *

    + *

    + *
    + * Query<Customer> query = server.find(Customer.class)
    + *     .fetch("contacts", new FetchConfig().query(2))
    + *     .where().gt("id", 0)
    + *     .orderBy("id")
    + *     .setMaxRows(2);
    + *
    + * query.findEachWhile((Customer customer) -> {
    + *
    + *     // do something with customer
    + *     System.out.println("-- visit " + customer);
    + *
    + *     // return true to continue processing or false to stop
    + *     return (customer.getId() < 40);
    + * });
    + * 
    + * + * @param the type of entity bean being queried. + */ +public interface QueryEachWhileConsumer { + + /** + * Process the bean and return true if you want to continue processing more + * beans. Return false if you want to stop processing further. + * + * @param bean the entity bean to process + * @return true to continue processing more beans or false to stop. + */ + boolean accept(T bean); +} diff --git a/src/main/java/com/avaje/ebean/QueryIterator.java b/src/main/java/com/avaje/ebean/QueryIterator.java index f3754fa93..589c352c5 100644 --- a/src/main/java/com/avaje/ebean/QueryIterator.java +++ b/src/main/java/com/avaje/ebean/QueryIterator.java @@ -1,60 +1,60 @@ -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). - *

    - * - *
    - * 
    - * 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. - */ - public boolean hasNext(); - - /** - * Returns the next element in the iteration. - */ - public T next(); - - /** - * Remove is not allowed. - */ - public void remove(); - - /** - * Close the underlying resources held by this iterator. - */ - public void close(); -} +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). + *

    + * + *
    + * 
    + * 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/ebean/QueryResultVisitor.java b/src/main/java/com/avaje/ebean/QueryResultVisitor.java index 586622a84..0c99c8a44 100644 --- a/src/main/java/com/avaje/ebean/QueryResultVisitor.java +++ b/src/main/java/com/avaje/ebean/QueryResultVisitor.java @@ -1,49 +1,49 @@ -package com.avaje.ebean; - -/** - * Used to process a query result one bean at a time via a callback to this - * visitor. - *

    - * If you wish to stop further processing return false from the accept method. - *

    - *

    - * Unlike findList() and findSet() using a QueryResultVisitor does not require - * all the beans in the query result to be held in memory at once. This makes - * QueryResultVisitor useful for processing large queries. - *

    - * - *
    - * 
    - * Query<Customer> query = server.find(Customer.class)
    - *     .fetch("contacts", new FetchConfig().query(2))
    - *     .where().gt("id", 0)
    - *     .orderBy("id")
    - *     .setMaxRows(2);
    - * 
    - * query.findVisit(new QueryResultVisitor<Customer>() {
    - * 
    - *   public boolean accept(Customer customer) {
    - *     // do something with customer
    - *     System.out.println("-- visit " + customer);
    - *     return true;
    - *   }
    - * });
    - * 
    - * - * @author rbygrave - * - * @param - * the type of entity bean being queried. - */ -public interface QueryResultVisitor { - - /** - * Process the bean and return true if you want to continue processing more - * beans. Return false if you want to stop processing further. - * - * @param bean - * the entity bean to process - * @return true to continue processing or false to stop. - */ - public boolean accept(T bean); -} +package com.avaje.ebean; + +/** + * Used to process a query result one bean at a time via a callback to this + * visitor. + *

    + * If you wish to stop further processing return false from the accept method. + *

    + *

    + * Unlike findList() and findSet() using a QueryResultVisitor does not require + * all the beans in the query result to be held in memory at once. This makes + * QueryResultVisitor useful for processing large queries. + *

    + * + *
    + * 
    + * Query<Customer> query = server.find(Customer.class)
    + *     .fetch("contacts", new FetchConfig().query(2))
    + *     .where().gt("id", 0)
    + *     .orderBy("id")
    + *     .setMaxRows(2);
    + * 
    + * query.findVisit(new QueryResultVisitor<Customer>() {
    + * 
    + *   public boolean accept(Customer customer) {
    + *     // do something with customer
    + *     System.out.println("-- visit " + customer);
    + *     return true;
    + *   }
    + * });
    + * 
    + * + * @author rbygrave + * + * @param + * the type of entity bean being queried. + */ +public interface QueryResultVisitor { + + /** + * Process the bean and return true if you want to continue processing more + * beans. Return false if you want to stop processing further. + * + * @param bean + * the entity bean to process + * @return true to continue processing or false to stop. + */ + boolean accept(T bean); +} diff --git a/src/main/java/com/avaje/ebean/RawSql.java b/src/main/java/com/avaje/ebean/RawSql.java index 865dd0b4f..320a6f20e 100644 --- a/src/main/java/com/avaje/ebean/RawSql.java +++ b/src/main/java/com/avaje/ebean/RawSql.java @@ -1,692 +1,692 @@ -package com.avaje.ebean; - -import java.io.Serializable; -import java.sql.ResultSet; -import java.util.*; - -import com.avaje.ebean.util.CamelCaseHelper; - -/** - * Used to build object graphs based on a raw SQL statement (rather than - * generated by Ebean). - *

    - * If you don't want to build object graphs you can use {@link SqlQuery} instead - * which returns {@link SqlRow} objects rather than entity beans. - *

    - *

    - * Unparsed RawSql: - *

    - *

    - * When RawSql is created via {@link RawSqlBuilder#unparsed(String)} then Ebean can not - * modify the SQL at all. It can't add any extra expressions into the SQL. - *

    - *

    - * Parsed RawSql: - *

    - *

    - * When RawSql is created via {@link RawSqlBuilder#parse(String)} then Ebean will parse the - * SQL and find places in the SQL where it can add extra where expressions, add - * extra having expressions or replace the order by clause. If you want to - * explicitly tell Ebean where these insertion points are you can place special - * strings into your SQL ({@code ${where}} or {@code ${andWhere}} and {@code ${having}} or - * {@code ${andHaving})}. - *

    - *

    - * If the SQL already includes a WHERE clause put in {@code ${andWhere}} in the location - * you want Ebean to add any extra where expressions. If the SQL doesn't have a - * WHERE clause put {@code ${where}} in instead. Similarly you can put in {@code ${having}} or - * {@code ${andHaving}} where you want Ebean put add extra having expressions. - *

    - *

    - * Aggregates: - *

    - *

    - * Often RawSql will be used with Aggregate functions (sum, avg, max etc). The - * follow example shows an example based on Total Order Amount - - * sum(d.order_qty*d.unit_price). - *

    - *

    - * We can use a OrderAggregate bean that has a @Sql to indicate it is based - * on RawSql and not based on a real DB Table or DB View. It has some properties - * to hold the values for the aggregate functions (sum etc) and a @OneToOne - * to Order. - *

    - * - *

    Example OrderAggregate

    - * - *
    {@code
    - *  ...
    - *  // @Sql indicates to that this bean
    - *  // is based on RawSql rather than a table
    - * 
    - * @Entity
    - * @Sql
    - * public class OrderAggregate {
    - * 
    - *  @OneToOne
    - *  Order order;
    - *      
    - *  Double totalAmount;
    - *  
    - *  Double totalItems;
    - *  
    - *  // getters and setters
    - *  ...
    - *
    - * }
    - * - *

    Example 1:

    - * - *
    {@code
    - *
    - *   String sql = " select order_id, o.status, c.id, c.name, sum(d.order_qty*d.unit_price) as totalAmount"
    - *     + " from o_order o"
    - *     + " join o_customer c on c.id = o.kcustomer_id "
    - *     + " join o_order_detail d on d.order_id = o.id " + " group by order_id, o.status ";
    - * 
    - *   RawSql rawSql = RawSqlBuilder.parse(sql)
    - *     // map the sql result columns to bean properties
    - *     .columnMapping("order_id", "order.id")
    - *     .columnMapping("o.status", "order.status")
    - *     .columnMapping("c.id", "order.customer.id")
    - *     .columnMapping("c.name", "order.customer.name")
    - *     // we don't need to map this one due to the sql column alias
    - *     // .columnMapping("sum(d.order_qty*d.unit_price)", "totalAmount")
    - *     .create();
    - * 
    - *   List list = Ebean.find(OrderAggregate.class)
    - *       .setRawSql(rawSql)
    - *       .where().gt("order.id", 0)
    - *       .having().gt("totalAmount", 20)
    - *       .findList();
    - * 
    - *
    - * }
    - * - *

    Example 2:

    - * - *

    - * The following example uses a FetchConfig().query() so that after the initial - * RawSql query is executed Ebean executes a secondary query to fetch the - * associated order status, orderDate along with the customer name. - *

    - * - *
    {@code
    - *
    - *  String sql = " select order_id, 'ignoreMe', sum(d.order_qty*d.unit_price) as totalAmount "
    - *     + " from o_order_detail d"
    - *     + " group by order_id ";
    - * 
    - *   RawSql rawSql = RawSqlBuilder.parse(sql)
    - *     .columnMapping("order_id", "order.id")
    - *     .columnMappingIgnore("'ignoreMe'")
    - *     .create();
    - * 
    - *   List orders = Ebean.find(OrderAggregate.class)
    - *     .setRawSql(rawSql)
    - *     .fetch("order", "status,orderDate", new FetchConfig().query())
    - *     .fetch("order.customer", "name")
    - *     .where().gt("order.id", 0)
    - *     .having().gt("totalAmount", 20)
    - *     .order().desc("totalAmount")
    - *     .setMaxRows(10)
    - *     .findList();
    - * 
    - * }
    - * - * - *

    Example 3: tableAliasMapping

    - *

    - * Instead of mapping each column you can map each table alias to a path using tableAliasMapping(). - *

    - *
    {@code
    - *
    - *   String rs = "select o.id, o.status, c.id, c.name, "+
    - *               " d.id, d.order_qty, p.id, p.name " +
    - *               "from o_order o join o_customer c on c.id = o.kcustomer_id " +
    - *               "join o_order_detail d on d.order_id = o.id  " +
    - *               "join o_product p on p.id = d.product_id  " +
    - *               "where o.id <= :maxOrderId  and p.id = :productId "+
    - *               "order by o.id, d.id asc";
    - *
    - *  RawSql rawSql = RawSqlBuilder.parse(rs)
    - *       .tableAliasMapping("c", "customer")
    - *       .tableAliasMapping("d", "details")
    - *       .tableAliasMapping("p", "details.product")
    - *       .create();
    - *
    - *  List ordersFromRaw = Ebean.find(Order.class)
    - *       .setRawSql(rawSql)
    - *       .setParameter("maxOrderId", 2)
    - *       .setParameter("productId", 1)
    - *       .findList();
    - *
    - * }
    - * - * - *

    - * Note that lazy loading also works with object graphs built with RawSql. - *

    - * - */ -public final class RawSql implements Serializable { - - private static final long serialVersionUID = 1L; - - private final ResultSet resultSet; - - private final Sql sql; - - private final ColumnMapping columnMapping; - - /** - * Construct with a ResultSet and properties that the columns map to. - *

    - * The properties listed in the propertyNames must be in the same order as the columns in the - * resultSet. - *

    - * When a query executes this RawSql object then it will close the resultSet. - */ - public RawSql(ResultSet resultSet, String... propertyNames) { - this.resultSet = resultSet; - this.sql = null; - this.columnMapping = new ColumnMapping(propertyNames); - } - - protected RawSql(ResultSet resultSet, Sql sql, ColumnMapping columnMapping) { - this.resultSet = resultSet; - this.sql = sql; - this.columnMapping = columnMapping; - } - - /** - * Return the Sql either unparsed or in parsed (broken up) form. - */ - public Sql getSql() { - return sql; - } - - - /** - * Return the resultSet if this is a ResultSet based RawSql. - */ - public ResultSet getResultSet() { - return resultSet; - } - - /** - * Return the column mapping for the SQL columns to bean properties. - */ - public ColumnMapping getColumnMapping() { - return columnMapping; - } - - /** - * Return the hash for this query. - */ - public int queryHash() { - if (resultSet != null) { - return 31 * columnMapping.queryHash(); - } - return 31 * sql.queryHash() + columnMapping.queryHash(); - } - - /** - * Represents the sql part of the query. For parsed RawSql the sql is broken - * up so that Ebean can insert extra WHERE and HAVING expressions into the - * SQL. - */ - public static final class Sql implements Serializable { - - private static final long serialVersionUID = 1L; - - private final boolean parsed; - - private final String unparsedSql; - - private final String preFrom; - - private final String preWhere; - - private final boolean andWhereExpr; - - private final String preHaving; - - private final boolean andHavingExpr; - - private final String orderByPrefix; - - private final String orderBy; - - private final boolean distinct; - - private final int queryHashCode; - - /** - * Construct for unparsed SQL. - */ - protected Sql(String unparsedSql) { - this.queryHashCode = unparsedSql.hashCode(); - this.parsed = false; - this.unparsedSql = unparsedSql; - this.preFrom = null; - this.preHaving = null; - this.preWhere = null; - this.andHavingExpr = false; - this.andWhereExpr = false; - this.orderByPrefix = null; - this.orderBy = null; - this.distinct = false; - } - - /** - * Construct for parsed SQL. - */ - protected Sql(int queryHashCode, String preFrom, String preWhere, boolean andWhereExpr, - String preHaving, boolean andHavingExpr, String orderByPrefix, String orderBy, boolean distinct) { - - this.queryHashCode = queryHashCode; - this.parsed = true; - this.unparsedSql = null; - this.preFrom = preFrom; - this.preHaving = preHaving; - this.preWhere = preWhere; - this.andHavingExpr = andHavingExpr; - this.andWhereExpr = andWhereExpr; - this.orderByPrefix = orderByPrefix; - this.orderBy = orderBy; - this.distinct = distinct; - } - - /** - * Return a hash for this query. - */ - public int queryHash() { - return queryHashCode; - } - - public String toString() { - if (!parsed) { - return "unparsed[" + unparsedSql + "]"; - } - return "select[" + preFrom + "] preWhere[" + preWhere + "] preHaving[" + preHaving - + "] orderBy[" + orderBy + "]"; - } - - public boolean isDistinct() { - return distinct; - } - - /** - * Return true if the SQL is left completely unmodified. - *

    - * This means Ebean can't add WHERE or HAVING expressions into the query - - * it will be left completely unmodified. - *

    - */ - public boolean isParsed() { - return parsed; - } - - /** - * Return the SQL when it is unparsed. - */ - public String getUnparsedSql() { - return unparsedSql; - } - - /** - * Return the SQL prior to FROM clause. - */ - public String getPreFrom() { - return preFrom; - } - - /** - * Return the SQL prior to WHERE clause. - */ - public String getPreWhere() { - return preWhere; - } - - /** - * Return true if there is already a WHERE clause and any extra where - * expressions start with AND. - */ - public boolean isAndWhereExpr() { - return andWhereExpr; - } - - /** - * Return the SQL prior to HAVING clause. - */ - public String getPreHaving() { - return preHaving; - } - - /** - * Return true if there is already a HAVING clause and any extra having - * expressions start with AND. - */ - public boolean isAndHavingExpr() { - return andHavingExpr; - } - - /** - * Return the 'order by' keywords. - * This can contain additional keywords, for example 'order siblings by' as Oracle syntax. - */ - public String getOrderByPrefix() { - return (orderByPrefix == null) ? "order by" : orderByPrefix; - } - - /** - * Return the SQL ORDER BY clause. - */ - public String getOrderBy() { - return orderBy; - } - - } - - /** - * Defines the column mapping for raw sql DB columns to bean properties. - */ - public static final class ColumnMapping implements Serializable { - - private static final long serialVersionUID = 1L; - - private final LinkedHashMap dbColumnMap; - - private final Map propertyMap; - - private final Map propertyColumnMap; - - private final boolean parsed; - - private final boolean immutable; - - private final int queryHashCode; - - /** - * Construct from parsed sql where the columns have been identified. - */ - protected ColumnMapping(List columns) { - this.queryHashCode = 0; - this.immutable = false; - this.parsed = true; - this.propertyMap = null; - this.propertyColumnMap = null; - this.dbColumnMap = new LinkedHashMap(); - for (int i = 0; i < columns.size(); i++) { - Column c = columns.get(i); - dbColumnMap.put(c.getDbColumn(), c); - } - } - - /** - * Construct for unparsed sql. - */ - protected ColumnMapping() { - this.queryHashCode = 0; - this.immutable = false; - this.parsed = false; - this.propertyMap = null; - this.propertyColumnMap = null; - this.dbColumnMap = new LinkedHashMap(); - } - - /** - * Construct for ResultSet use. - */ - protected ColumnMapping(String... propertyNames) { - this.immutable = false; - this.parsed = false; - this.propertyMap = null; - //this.propertyColumnMap = null; - this.dbColumnMap = new LinkedHashMap(); - - int hc = 31; - int pos = 0; - for (String prop : propertyNames) { - hc = 31 * hc + prop.hashCode(); - dbColumnMap.put(prop, new Column(pos++, prop, null, prop)); - } - propertyColumnMap = dbColumnMap; - this.queryHashCode = hc; - } - - /** - * Construct an immutable ColumnMapping based on collected information. - */ - protected ColumnMapping(boolean parsed, LinkedHashMap dbColumnMap) { - this.immutable = true; - this.parsed = parsed; - this.dbColumnMap = dbColumnMap; - - int hc = ColumnMapping.class.getName().hashCode(); - - HashMap pcMap = new HashMap(); - HashMap pMap = new HashMap(); - - for (Column c : dbColumnMap.values()) { - pMap.put(c.getPropertyName(), c.getDbColumn()); - pcMap.put(c.getPropertyName(), c); - hc = 31 * hc + ((c.getPropertyName() == null) ? 0 : c.getPropertyName().hashCode()); - hc = 31 * hc + ((c.getDbColumn() == null) ? 0 : c.getDbColumn().hashCode()); - } - this.propertyMap = Collections.unmodifiableMap(pMap); - this.propertyColumnMap = Collections.unmodifiableMap(pcMap); - this.queryHashCode = hc; - } - - /** - * Creates an immutable copy of this ColumnMapping. - * - * @throws IllegalStateException - * when a propertyName has not been defined for a column. - */ - protected ColumnMapping createImmutableCopy() { - - for (Column c : dbColumnMap.values()) { - c.checkMapping(); - } - - return new ColumnMapping(parsed, dbColumnMap); - } - - protected void columnMapping(String dbColumn, String propertyName) { - - if (immutable) { - throw new IllegalStateException("Should never happen"); - } - if (!parsed) { - int pos = dbColumnMap.size(); - dbColumnMap.put(dbColumn, new Column(pos, dbColumn, null, propertyName)); - } else { - Column column = dbColumnMap.get(dbColumn); - if (column == null) { - String msg = "DB Column [" + dbColumn + "] not found in mapping. Expecting one of [" - + dbColumnMap.keySet() + "]"; - throw new IllegalArgumentException(msg); - } - column.setPropertyName(propertyName); - } - } - - /** - * Return the query hash for this column mapping. - */ - public int queryHash() { - if (queryHashCode == 0) { - throw new RuntimeException("Bug: queryHashCode == 0"); - } - return queryHashCode; - } - - /** - * Returns true if the Columns where supplied by parsing the sql select - * clause. - *

    - * In the case where the columns where parsed then we can do extra checks on - * the column mapping such as, is the column a valid one in the sql and - * whether all the columns in the sql have been mapped. - *

    - */ - public boolean isParsed() { - return parsed; - } - - /** - * Return the number of columns in this column mapping. - */ - public int size() { - return dbColumnMap.size(); - } - - /** - * Return the column mapping. - */ - protected Map mapping() { - return dbColumnMap; - } - - /** - * Return the mapping by DB column. - */ - public Map getMapping() { - return propertyMap; - } - - /** - * Return the index position by bean property name. - */ - public int getIndexPosition(String property) { - Column c = propertyColumnMap.get(property); - return c == null ? -1 : c.getIndexPos(); - } - - /** - * Return an iterator of the Columns. - */ - public Iterator getColumns() { - return dbColumnMap.values().iterator(); - } - - /** - * Modify any column mappings with the given table alias to have the path prefix. - *

    - * For example modify all mappings with table alias "c" to have the path prefix "customer". - *

    - */ - public void tableAliasMapping(String tableAlias, String path) { - - String startMatch = tableAlias+"."; - for (Map.Entry entry : dbColumnMap.entrySet()) { - if (entry.getKey().startsWith(startMatch)) { - entry.getValue().tableAliasMapping(path); - } - } - } - - /** - * A Column of the RawSql that is mapped to a bean property (or ignored). - */ - public static class Column implements Serializable { - - private static final long serialVersionUID = 1L; - private final int indexPos; - private final String dbColumn; - - private final String dbAlias; - - private String propertyName; - - /** - * Construct a Column. - */ - public Column(int indexPos, String dbColumn, String dbAlias) { - this(indexPos, dbColumn, dbAlias, derivePropertyName(dbAlias, dbColumn)); - } - - private Column(int indexPos, String dbColumn, String dbAlias, String propertyName) { - this.indexPos = indexPos; - this.dbColumn = dbColumn; - this.dbAlias = dbAlias; - if (propertyName == null && dbAlias != null) { - this.propertyName = dbAlias; - } else { - this.propertyName = propertyName; - } - } - - private static String derivePropertyName(String dbAlias, String dbColumn) { - if (dbAlias != null) { - return dbAlias; - } - int dotPos = dbColumn.indexOf('.'); - if (dotPos > -1) { - dbColumn = dbColumn.substring(dotPos + 1); - } - return CamelCaseHelper.toCamelFromUnderscore(dbColumn); - } - - private void checkMapping() { - if (propertyName == null) { - String msg = "No propertyName defined (Column mapping) for dbColumn [" + dbColumn + "]"; - throw new IllegalStateException(msg); - } - } - - public String toString() { - return dbColumn + "->" + propertyName; - } - - /** - * Return the index position of this column. - */ - public int getIndexPos() { - return indexPos; - } - - /** - * Return the DB column name including table alias (if it has one). - */ - public String getDbColumn() { - return dbColumn; - } - - /** - * Return the DB column alias (if it has one). - */ - public String getDbAlias() { - return dbAlias; - } - - /** - * Return the bean property this column is mapped to. - */ - public String getPropertyName() { - return propertyName; - } - - /** - * Set the property name mapped to this db column. - */ - private void setPropertyName(String propertyName) { - this.propertyName = propertyName; - } - - /** - * Prepend the path to the property name. - *

    - * For example if path is "customer" then "name" becomes "customer.name". - */ - public void tableAliasMapping(String path) { - if (path != null) { - propertyName = path + "." + propertyName; - } - } - } - } -} +package com.avaje.ebean; + +import java.io.Serializable; +import java.sql.ResultSet; +import java.util.*; + +import com.avaje.ebean.util.CamelCaseHelper; + +/** + * Used to build object graphs based on a raw SQL statement (rather than + * generated by Ebean). + *

    + * If you don't want to build object graphs you can use {@link SqlQuery} instead + * which returns {@link SqlRow} objects rather than entity beans. + *

    + *

    + * Unparsed RawSql: + *

    + *

    + * When RawSql is created via {@link RawSqlBuilder#unparsed(String)} then Ebean can not + * modify the SQL at all. It can't add any extra expressions into the SQL. + *

    + *

    + * Parsed RawSql: + *

    + *

    + * When RawSql is created via {@link RawSqlBuilder#parse(String)} then Ebean will parse the + * SQL and find places in the SQL where it can add extra where expressions, add + * extra having expressions or replace the order by clause. If you want to + * explicitly tell Ebean where these insertion points are you can place special + * strings into your SQL ({@code ${where}} or {@code ${andWhere}} and {@code ${having}} or + * {@code ${andHaving})}. + *

    + *

    + * If the SQL already includes a WHERE clause put in {@code ${andWhere}} in the location + * you want Ebean to add any extra where expressions. If the SQL doesn't have a + * WHERE clause put {@code ${where}} in instead. Similarly you can put in {@code ${having}} or + * {@code ${andHaving}} where you want Ebean put add extra having expressions. + *

    + *

    + * Aggregates: + *

    + *

    + * Often RawSql will be used with Aggregate functions (sum, avg, max etc). The + * follow example shows an example based on Total Order Amount - + * sum(d.order_qty*d.unit_price). + *

    + *

    + * We can use a OrderAggregate bean that has a @Sql to indicate it is based + * on RawSql and not based on a real DB Table or DB View. It has some properties + * to hold the values for the aggregate functions (sum etc) and a @OneToOne + * to Order. + *

    + * + *

    Example OrderAggregate

    + * + *
    {@code
    + *  ...
    + *  // @Sql indicates to that this bean
    + *  // is based on RawSql rather than a table
    + * 
    + * @Entity
    + * @Sql
    + * public class OrderAggregate {
    + * 
    + *  @OneToOne
    + *  Order order;
    + *      
    + *  Double totalAmount;
    + *  
    + *  Double totalItems;
    + *  
    + *  // getters and setters
    + *  ...
    + *
    + * }
    + * + *

    Example 1:

    + * + *
    {@code
    + *
    + *   String sql = " select order_id, o.status, c.id, c.name, sum(d.order_qty*d.unit_price) as totalAmount"
    + *     + " from o_order o"
    + *     + " join o_customer c on c.id = o.kcustomer_id "
    + *     + " join o_order_detail d on d.order_id = o.id " + " group by order_id, o.status ";
    + * 
    + *   RawSql rawSql = RawSqlBuilder.parse(sql)
    + *     // map the sql result columns to bean properties
    + *     .columnMapping("order_id", "order.id")
    + *     .columnMapping("o.status", "order.status")
    + *     .columnMapping("c.id", "order.customer.id")
    + *     .columnMapping("c.name", "order.customer.name")
    + *     // we don't need to map this one due to the sql column alias
    + *     // .columnMapping("sum(d.order_qty*d.unit_price)", "totalAmount")
    + *     .create();
    + * 
    + *   List list = Ebean.find(OrderAggregate.class)
    + *       .setRawSql(rawSql)
    + *       .where().gt("order.id", 0)
    + *       .having().gt("totalAmount", 20)
    + *       .findList();
    + * 
    + *
    + * }
    + * + *

    Example 2:

    + * + *

    + * The following example uses a FetchConfig().query() so that after the initial + * RawSql query is executed Ebean executes a secondary query to fetch the + * associated order status, orderDate along with the customer name. + *

    + * + *
    {@code
    + *
    + *  String sql = " select order_id, 'ignoreMe', sum(d.order_qty*d.unit_price) as totalAmount "
    + *     + " from o_order_detail d"
    + *     + " group by order_id ";
    + * 
    + *   RawSql rawSql = RawSqlBuilder.parse(sql)
    + *     .columnMapping("order_id", "order.id")
    + *     .columnMappingIgnore("'ignoreMe'")
    + *     .create();
    + * 
    + *   List orders = Ebean.find(OrderAggregate.class)
    + *     .setRawSql(rawSql)
    + *     .fetch("order", "status,orderDate", new FetchConfig().query())
    + *     .fetch("order.customer", "name")
    + *     .where().gt("order.id", 0)
    + *     .having().gt("totalAmount", 20)
    + *     .order().desc("totalAmount")
    + *     .setMaxRows(10)
    + *     .findList();
    + * 
    + * }
    + * + * + *

    Example 3: tableAliasMapping

    + *

    + * Instead of mapping each column you can map each table alias to a path using tableAliasMapping(). + *

    + *
    {@code
    + *
    + *   String rs = "select o.id, o.status, c.id, c.name, "+
    + *               " d.id, d.order_qty, p.id, p.name " +
    + *               "from o_order o join o_customer c on c.id = o.kcustomer_id " +
    + *               "join o_order_detail d on d.order_id = o.id  " +
    + *               "join o_product p on p.id = d.product_id  " +
    + *               "where o.id <= :maxOrderId  and p.id = :productId "+
    + *               "order by o.id, d.id asc";
    + *
    + *  RawSql rawSql = RawSqlBuilder.parse(rs)
    + *       .tableAliasMapping("c", "customer")
    + *       .tableAliasMapping("d", "details")
    + *       .tableAliasMapping("p", "details.product")
    + *       .create();
    + *
    + *  List ordersFromRaw = Ebean.find(Order.class)
    + *       .setRawSql(rawSql)
    + *       .setParameter("maxOrderId", 2)
    + *       .setParameter("productId", 1)
    + *       .findList();
    + *
    + * }
    + * + * + *

    + * Note that lazy loading also works with object graphs built with RawSql. + *

    + * + */ +public final class RawSql implements Serializable { + + private static final long serialVersionUID = 1L; + + private final ResultSet resultSet; + + private final Sql sql; + + private final ColumnMapping columnMapping; + + /** + * Construct with a ResultSet and properties that the columns map to. + *

    + * The properties listed in the propertyNames must be in the same order as the columns in the + * resultSet. + *

    + * When a query executes this RawSql object then it will close the resultSet. + */ + public RawSql(ResultSet resultSet, String... propertyNames) { + this.resultSet = resultSet; + this.sql = null; + this.columnMapping = new ColumnMapping(propertyNames); + } + + protected RawSql(ResultSet resultSet, Sql sql, ColumnMapping columnMapping) { + this.resultSet = resultSet; + this.sql = sql; + this.columnMapping = columnMapping; + } + + /** + * Return the Sql either unparsed or in parsed (broken up) form. + */ + public Sql getSql() { + return sql; + } + + + /** + * Return the resultSet if this is a ResultSet based RawSql. + */ + public ResultSet getResultSet() { + return resultSet; + } + + /** + * Return the column mapping for the SQL columns to bean properties. + */ + public ColumnMapping getColumnMapping() { + return columnMapping; + } + + /** + * Return the hash for this query. + */ + public int queryHash() { + if (resultSet != null) { + return 31 * columnMapping.queryHash(); + } + return 31 * sql.queryHash() + columnMapping.queryHash(); + } + + /** + * Represents the sql part of the query. For parsed RawSql the sql is broken + * up so that Ebean can insert extra WHERE and HAVING expressions into the + * SQL. + */ + public static final class Sql implements Serializable { + + private static final long serialVersionUID = 1L; + + private final boolean parsed; + + private final String unparsedSql; + + private final String preFrom; + + private final String preWhere; + + private final boolean andWhereExpr; + + private final String preHaving; + + private final boolean andHavingExpr; + + private final String orderByPrefix; + + private final String orderBy; + + private final boolean distinct; + + private final int queryHashCode; + + /** + * Construct for unparsed SQL. + */ + protected Sql(String unparsedSql) { + this.queryHashCode = unparsedSql.hashCode(); + this.parsed = false; + this.unparsedSql = unparsedSql; + this.preFrom = null; + this.preHaving = null; + this.preWhere = null; + this.andHavingExpr = false; + this.andWhereExpr = false; + this.orderByPrefix = null; + this.orderBy = null; + this.distinct = false; + } + + /** + * Construct for parsed SQL. + */ + protected Sql(int queryHashCode, String preFrom, String preWhere, boolean andWhereExpr, + String preHaving, boolean andHavingExpr, String orderByPrefix, String orderBy, boolean distinct) { + + this.queryHashCode = queryHashCode; + this.parsed = true; + this.unparsedSql = null; + this.preFrom = preFrom; + this.preHaving = preHaving; + this.preWhere = preWhere; + this.andHavingExpr = andHavingExpr; + this.andWhereExpr = andWhereExpr; + this.orderByPrefix = orderByPrefix; + this.orderBy = orderBy; + this.distinct = distinct; + } + + /** + * Return a hash for this query. + */ + public int queryHash() { + return queryHashCode; + } + + public String toString() { + if (!parsed) { + return "unparsed[" + unparsedSql + "]"; + } + return "select[" + preFrom + "] preWhere[" + preWhere + "] preHaving[" + preHaving + + "] orderBy[" + orderBy + "]"; + } + + public boolean isDistinct() { + return distinct; + } + + /** + * Return true if the SQL is left completely unmodified. + *

    + * This means Ebean can't add WHERE or HAVING expressions into the query - + * it will be left completely unmodified. + *

    + */ + public boolean isParsed() { + return parsed; + } + + /** + * Return the SQL when it is unparsed. + */ + public String getUnparsedSql() { + return unparsedSql; + } + + /** + * Return the SQL prior to FROM clause. + */ + public String getPreFrom() { + return preFrom; + } + + /** + * Return the SQL prior to WHERE clause. + */ + public String getPreWhere() { + return preWhere; + } + + /** + * Return true if there is already a WHERE clause and any extra where + * expressions start with AND. + */ + public boolean isAndWhereExpr() { + return andWhereExpr; + } + + /** + * Return the SQL prior to HAVING clause. + */ + public String getPreHaving() { + return preHaving; + } + + /** + * Return true if there is already a HAVING clause and any extra having + * expressions start with AND. + */ + public boolean isAndHavingExpr() { + return andHavingExpr; + } + + /** + * Return the 'order by' keywords. + * This can contain additional keywords, for example 'order siblings by' as Oracle syntax. + */ + public String getOrderByPrefix() { + return (orderByPrefix == null) ? "order by" : orderByPrefix; + } + + /** + * Return the SQL ORDER BY clause. + */ + public String getOrderBy() { + return orderBy; + } + + } + + /** + * Defines the column mapping for raw sql DB columns to bean properties. + */ + public static final class ColumnMapping implements Serializable { + + private static final long serialVersionUID = 1L; + + private final LinkedHashMap dbColumnMap; + + private final Map propertyMap; + + private final Map propertyColumnMap; + + private final boolean parsed; + + private final boolean immutable; + + private final int queryHashCode; + + /** + * Construct from parsed sql where the columns have been identified. + */ + protected ColumnMapping(List columns) { + this.queryHashCode = 0; + this.immutable = false; + this.parsed = true; + this.propertyMap = null; + this.propertyColumnMap = null; + this.dbColumnMap = new LinkedHashMap(); + for (int i = 0; i < columns.size(); i++) { + Column c = columns.get(i); + dbColumnMap.put(c.getDbColumn(), c); + } + } + + /** + * Construct for unparsed sql. + */ + protected ColumnMapping() { + this.queryHashCode = 0; + this.immutable = false; + this.parsed = false; + this.propertyMap = null; + this.propertyColumnMap = null; + this.dbColumnMap = new LinkedHashMap(); + } + + /** + * Construct for ResultSet use. + */ + protected ColumnMapping(String... propertyNames) { + this.immutable = false; + this.parsed = false; + this.propertyMap = null; + //this.propertyColumnMap = null; + this.dbColumnMap = new LinkedHashMap(); + + int hc = 31; + int pos = 0; + for (String prop : propertyNames) { + hc = 31 * hc + prop.hashCode(); + dbColumnMap.put(prop, new Column(pos++, prop, null, prop)); + } + propertyColumnMap = dbColumnMap; + this.queryHashCode = hc; + } + + /** + * Construct an immutable ColumnMapping based on collected information. + */ + protected ColumnMapping(boolean parsed, LinkedHashMap dbColumnMap) { + this.immutable = true; + this.parsed = parsed; + this.dbColumnMap = dbColumnMap; + + int hc = ColumnMapping.class.getName().hashCode(); + + HashMap pcMap = new HashMap(); + HashMap pMap = new HashMap(); + + for (Column c : dbColumnMap.values()) { + pMap.put(c.getPropertyName(), c.getDbColumn()); + pcMap.put(c.getPropertyName(), c); + hc = 31 * hc + ((c.getPropertyName() == null) ? 0 : c.getPropertyName().hashCode()); + hc = 31 * hc + ((c.getDbColumn() == null) ? 0 : c.getDbColumn().hashCode()); + } + this.propertyMap = Collections.unmodifiableMap(pMap); + this.propertyColumnMap = Collections.unmodifiableMap(pcMap); + this.queryHashCode = hc; + } + + /** + * Creates an immutable copy of this ColumnMapping. + * + * @throws IllegalStateException + * when a propertyName has not been defined for a column. + */ + protected ColumnMapping createImmutableCopy() { + + for (Column c : dbColumnMap.values()) { + c.checkMapping(); + } + + return new ColumnMapping(parsed, dbColumnMap); + } + + protected void columnMapping(String dbColumn, String propertyName) { + + if (immutable) { + throw new IllegalStateException("Should never happen"); + } + if (!parsed) { + int pos = dbColumnMap.size(); + dbColumnMap.put(dbColumn, new Column(pos, dbColumn, null, propertyName)); + } else { + Column column = dbColumnMap.get(dbColumn); + if (column == null) { + String msg = "DB Column [" + dbColumn + "] not found in mapping. Expecting one of [" + + dbColumnMap.keySet() + "]"; + throw new IllegalArgumentException(msg); + } + column.setPropertyName(propertyName); + } + } + + /** + * Return the query hash for this column mapping. + */ + public int queryHash() { + if (queryHashCode == 0) { + throw new RuntimeException("Bug: queryHashCode == 0"); + } + return queryHashCode; + } + + /** + * Returns true if the Columns where supplied by parsing the sql select + * clause. + *

    + * In the case where the columns where parsed then we can do extra checks on + * the column mapping such as, is the column a valid one in the sql and + * whether all the columns in the sql have been mapped. + *

    + */ + public boolean isParsed() { + return parsed; + } + + /** + * Return the number of columns in this column mapping. + */ + public int size() { + return dbColumnMap.size(); + } + + /** + * Return the column mapping. + */ + protected Map mapping() { + return dbColumnMap; + } + + /** + * Return the mapping by DB column. + */ + public Map getMapping() { + return propertyMap; + } + + /** + * Return the index position by bean property name. + */ + public int getIndexPosition(String property) { + Column c = propertyColumnMap.get(property); + return c == null ? -1 : c.getIndexPos(); + } + + /** + * Return an iterator of the Columns. + */ + public Iterator getColumns() { + return dbColumnMap.values().iterator(); + } + + /** + * Modify any column mappings with the given table alias to have the path prefix. + *

    + * For example modify all mappings with table alias "c" to have the path prefix "customer". + *

    + */ + public void tableAliasMapping(String tableAlias, String path) { + + String startMatch = tableAlias+"."; + for (Map.Entry entry : dbColumnMap.entrySet()) { + if (entry.getKey().startsWith(startMatch)) { + entry.getValue().tableAliasMapping(path); + } + } + } + + /** + * A Column of the RawSql that is mapped to a bean property (or ignored). + */ + public static class Column implements Serializable { + + private static final long serialVersionUID = 1L; + private final int indexPos; + private final String dbColumn; + + private final String dbAlias; + + private String propertyName; + + /** + * Construct a Column. + */ + public Column(int indexPos, String dbColumn, String dbAlias) { + this(indexPos, dbColumn, dbAlias, derivePropertyName(dbAlias, dbColumn)); + } + + private Column(int indexPos, String dbColumn, String dbAlias, String propertyName) { + this.indexPos = indexPos; + this.dbColumn = dbColumn; + this.dbAlias = dbAlias; + if (propertyName == null && dbAlias != null) { + this.propertyName = dbAlias; + } else { + this.propertyName = propertyName; + } + } + + private static String derivePropertyName(String dbAlias, String dbColumn) { + if (dbAlias != null) { + return dbAlias; + } + int dotPos = dbColumn.indexOf('.'); + if (dotPos > -1) { + dbColumn = dbColumn.substring(dotPos + 1); + } + return CamelCaseHelper.toCamelFromUnderscore(dbColumn); + } + + private void checkMapping() { + if (propertyName == null) { + String msg = "No propertyName defined (Column mapping) for dbColumn [" + dbColumn + "]"; + throw new IllegalStateException(msg); + } + } + + public String toString() { + return dbColumn + "->" + propertyName; + } + + /** + * Return the index position of this column. + */ + public int getIndexPos() { + return indexPos; + } + + /** + * Return the DB column name including table alias (if it has one). + */ + public String getDbColumn() { + return dbColumn; + } + + /** + * Return the DB column alias (if it has one). + */ + public String getDbAlias() { + return dbAlias; + } + + /** + * Return the bean property this column is mapped to. + */ + public String getPropertyName() { + return propertyName; + } + + /** + * Set the property name mapped to this db column. + */ + private void setPropertyName(String propertyName) { + this.propertyName = propertyName; + } + + /** + * Prepend the path to the property name. + *

    + * For example if path is "customer" then "name" becomes "customer.name". + */ + public void tableAliasMapping(String path) { + if (path != null) { + propertyName = path + "." + propertyName; + } + } + } + } +} diff --git a/src/main/java/com/avaje/ebean/RawSqlBuilder.java b/src/main/java/com/avaje/ebean/RawSqlBuilder.java index 08a8c27f0..8baae3dfb 100644 --- a/src/main/java/com/avaje/ebean/RawSqlBuilder.java +++ b/src/main/java/com/avaje/ebean/RawSqlBuilder.java @@ -1,132 +1,132 @@ -package com.avaje.ebean; - -import java.sql.ResultSet; - -import com.avaje.ebean.RawSql.ColumnMapping; -import com.avaje.ebean.RawSql.Sql; - -/** - * Builds RawSql instances from a SQL string and column mappings. - *

    - * Note that RawSql can also be defined in ebean-orm.xml files and be used as a - * named query. - *

    - * - * @see RawSql - */ -public class RawSqlBuilder { - - /** - * Special property name assigned to a DB column that should be ignored. - */ - public static final String IGNORE_COLUMN = "$$_IGNORE_COLUMN_$$"; - - private final ResultSet resultSet; - - private final Sql sql; - - private final ColumnMapping columnMapping; - - /** - * Create and return a RawSql object based on the resultSet and list of properties the columns in - * the resultSet map to. - *

    - * The properties listed in the propertyNames must be in the same order as the columns in the - * resultSet. - */ - public static RawSql resultSet(ResultSet resultSet, String... propertyNames) { - return new RawSql(resultSet, propertyNames); - } - - /** - * Return an unparsed RawSqlBuilder. Unlike a parsed one this query can not be - * modified - so no additional WHERE or HAVING expressions can be added to - * this query. - */ - public static RawSqlBuilder unparsed(String sql) { - - Sql s = new Sql(sql); - return new RawSqlBuilder(s, new ColumnMapping()); - } - - /** - * Return a RawSqlBuilder parsing the sql. - *

    - * The sql statement will be parsed so that Ebean can determine where it can - * insert additional WHERE or HAVING expressions. - *

    - *

    - * Additionally the selected columns are parsed to determine the column - * ordering. This also means additional checks can be made with the column - * mapping - specifically we can check that all columns are mapped and that - * correct column names are entered into the mapping. - *

    - */ - public static RawSqlBuilder parse(String sql) { - - Sql sql2 = DRawSqlParser.parse(sql); - String select = sql2.getPreFrom(); - - ColumnMapping mapping = DRawSqlColumnsParser.parse(select); - return new RawSqlBuilder(sql2, mapping); - } - - private RawSqlBuilder(Sql sql, ColumnMapping columnMapping) { - this.sql = sql; - this.columnMapping = columnMapping; - this.resultSet = null; - } - - /** - * Set the mapping of a DB Column to a bean property. - *

    - * For Unparsed SQL the columnMapping MUST be defined in the same order that - * the columns appear in the SQL statement. - *

    - * - * @param dbColumn - * the DB column that we are mapping to a bean property - * @param propertyName - * the bean property that we are mapping the DB column to. - */ - public RawSqlBuilder columnMapping(String dbColumn, String propertyName) { - columnMapping.columnMapping(dbColumn, propertyName); - return this; - } - - /** - * Ignore this DB column. It is not mapped to any bean property. - */ - public RawSqlBuilder columnMappingIgnore(String dbColumn) { - return columnMapping(dbColumn, IGNORE_COLUMN); - } - - /** - * Modify any column mappings with the given table alias to have the path prefix. - *

    - * For example modify all mappings with table alias "c" to have the path prefix "customer". - *

    - */ - public RawSqlBuilder tableAliasMapping(String tableAlias, String path) { - columnMapping.tableAliasMapping(tableAlias, path); - return this; - } - - /** - * Create the immutable RawSql object. Do this after all the column mapping - * has been defined. - */ - public RawSql create() { - return new RawSql(resultSet, sql, columnMapping.createImmutableCopy()); - } - - /** - * Return the internal parsed Sql object (for testing). - */ - protected Sql getSql() { - return sql; - } - - - -} +package com.avaje.ebean; + +import java.sql.ResultSet; + +import com.avaje.ebean.RawSql.ColumnMapping; +import com.avaje.ebean.RawSql.Sql; + +/** + * Builds RawSql instances from a SQL string and column mappings. + *

    + * Note that RawSql can also be defined in ebean-orm.xml files and be used as a + * named query. + *

    + * + * @see RawSql + */ +public class RawSqlBuilder { + + /** + * Special property name assigned to a DB column that should be ignored. + */ + public static final String IGNORE_COLUMN = "$$_IGNORE_COLUMN_$$"; + + private final ResultSet resultSet; + + private final Sql sql; + + private final ColumnMapping columnMapping; + + /** + * Create and return a RawSql object based on the resultSet and list of properties the columns in + * the resultSet map to. + *

    + * The properties listed in the propertyNames must be in the same order as the columns in the + * resultSet. + */ + public static RawSql resultSet(ResultSet resultSet, String... propertyNames) { + return new RawSql(resultSet, propertyNames); + } + + /** + * Return an unparsed RawSqlBuilder. Unlike a parsed one this query can not be + * modified - so no additional WHERE or HAVING expressions can be added to + * this query. + */ + public static RawSqlBuilder unparsed(String sql) { + + Sql s = new Sql(sql); + return new RawSqlBuilder(s, new ColumnMapping()); + } + + /** + * Return a RawSqlBuilder parsing the sql. + *

    + * The sql statement will be parsed so that Ebean can determine where it can + * insert additional WHERE or HAVING expressions. + *

    + *

    + * Additionally the selected columns are parsed to determine the column + * ordering. This also means additional checks can be made with the column + * mapping - specifically we can check that all columns are mapped and that + * correct column names are entered into the mapping. + *

    + */ + public static RawSqlBuilder parse(String sql) { + + Sql sql2 = DRawSqlParser.parse(sql); + String select = sql2.getPreFrom(); + + ColumnMapping mapping = DRawSqlColumnsParser.parse(select); + return new RawSqlBuilder(sql2, mapping); + } + + private RawSqlBuilder(Sql sql, ColumnMapping columnMapping) { + this.sql = sql; + this.columnMapping = columnMapping; + this.resultSet = null; + } + + /** + * Set the mapping of a DB Column to a bean property. + *

    + * For Unparsed SQL the columnMapping MUST be defined in the same order that + * the columns appear in the SQL statement. + *

    + * + * @param dbColumn + * the DB column that we are mapping to a bean property + * @param propertyName + * the bean property that we are mapping the DB column to. + */ + public RawSqlBuilder columnMapping(String dbColumn, String propertyName) { + columnMapping.columnMapping(dbColumn, propertyName); + return this; + } + + /** + * Ignore this DB column. It is not mapped to any bean property. + */ + public RawSqlBuilder columnMappingIgnore(String dbColumn) { + return columnMapping(dbColumn, IGNORE_COLUMN); + } + + /** + * Modify any column mappings with the given table alias to have the path prefix. + *

    + * For example modify all mappings with table alias "c" to have the path prefix "customer". + *

    + */ + public RawSqlBuilder tableAliasMapping(String tableAlias, String path) { + columnMapping.tableAliasMapping(tableAlias, path); + return this; + } + + /** + * Create the immutable RawSql object. Do this after all the column mapping + * has been defined. + */ + public RawSql create() { + return new RawSql(resultSet, sql, columnMapping.createImmutableCopy()); + } + + /** + * Return the internal parsed Sql object (for testing). + */ + protected Sql getSql() { + return sql; + } + + + +} diff --git a/src/main/java/com/avaje/ebean/SqlFutureList.java b/src/main/java/com/avaje/ebean/SqlFutureList.java index 6df27b705..6490def78 100644 --- a/src/main/java/com/avaje/ebean/SqlFutureList.java +++ b/src/main/java/com/avaje/ebean/SqlFutureList.java @@ -1,47 +1,47 @@ -package com.avaje.ebean; - -import java.util.List; -import java.util.concurrent.Future; - -/** - * The SqlFutureList represents the result of a background SQL query execution. - * - *

    - * It extends the java.util.concurrent.Future. - *

    - * - *
    - *  // create a query
    - * String sql = ... ;
    - * SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
    - * 
    - *  // execute the query in a background thread
    - * SqlFutureList sqlFuture = sqlQuery.findFutureList();
    - * 
    - *  // do something else ... we will sleep
    - * Thread.sleep(3000);
    - * System.out.println("end of sleep");
    - * 
    - * if (!futureList.isDone()){
    - * 	// we can cancel the query execution
    - * 	futureList.cancel(true);
    - * }
    - * 
    - * System.out.println("and... done:"+futureList.isDone());
    - * 
    - * if (!futureList.isCancelled()){
    - * 	// wait for the query to finish and return the list
    - * 	List<SqlRow> list = futureList.get();
    - * 	System.out.println("list:"+list);
    - * }
    - * 
    - * 
    - * - * @author rob - * - */ -public interface SqlFutureList extends Future> { - - public SqlQuery getQuery(); - -} +package com.avaje.ebean; + +import java.util.List; +import java.util.concurrent.Future; + +/** + * The SqlFutureList represents the result of a background SQL query execution. + * + *

    + * It extends the java.util.concurrent.Future. + *

    + * + *
    + *  // create a query
    + * String sql = ... ;
    + * SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
    + * 
    + *  // execute the query in a background thread
    + * SqlFutureList sqlFuture = sqlQuery.findFutureList();
    + * 
    + *  // do something else ... we will sleep
    + * Thread.sleep(3000);
    + * System.out.println("end of sleep");
    + * 
    + * if (!futureList.isDone()){
    + * 	// we can cancel the query execution
    + * 	futureList.cancel(true);
    + * }
    + * 
    + * System.out.println("and... done:"+futureList.isDone());
    + * 
    + * if (!futureList.isCancelled()){
    + * 	// wait for the query to finish and return the list
    + * 	List<SqlRow> list = futureList.get();
    + * 	System.out.println("list:"+list);
    + * }
    + * 
    + * 
    + * + * @author rob + * + */ +public interface SqlFutureList extends Future> { + + SqlQuery getQuery(); + +} diff --git a/src/main/java/com/avaje/ebean/SqlQuery.java b/src/main/java/com/avaje/ebean/SqlQuery.java index 82dc0ad95..4852e575b 100644 --- a/src/main/java/com/avaje/ebean/SqlQuery.java +++ b/src/main/java/com/avaje/ebean/SqlQuery.java @@ -1,151 +1,151 @@ -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. - *

    - * Firstly note that you can use your own sql queries with entity beans - * by using the SqlSelect annotation. This should be your first approach when - * wanting to use your own SQL queries. - *

    - *

    - * If ORM Mapping is too tight and constraining for your problem then SqlQuery - * could be a good approach. - *

    - *

    - * The returned SqlRow objects are similar to a LinkedHashMap with some type - * conversion support added. - *

    - * - *
    - * // its typically a good idea to use a named query
    - * // and put the sql in the orm.xml instead of in your code
    - * 
    - * String sql = "select id, name from customer where name like :name and status_code = :status";
    - * 
    - * SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
    - * sqlQuery.setParameter("name", "Acme%");
    - * sqlQuery.setParameter("status", "ACTIVE");
    - * 
    - * // execute the query returning a List of MapBean objects
    - * List<SqlRow> list = sqlQuery.findList();
    - * 
    - * - */ -public interface SqlQuery extends Serializable { - - /** - * Cancel the query if support by the underlying database and driver. - *

    - * This must be called from a different thread to the one executing the query. - *

    - */ - public void cancel(); - - /** - * Execute the query returning a list. - */ - public List findList(); - - /** - * Execute the query returning a set. - */ - public Set findSet(); - - /** - * Execute the query returning a map. - */ - public Map findMap(); - - /** - * Execute the query returning a single row or null. - *

    - * If this query finds 2 or more rows then it will throw a - * PersistenceException. - *

    - */ - public SqlRow findUnique(); - - /** - * Execute find list SQL 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 list result of the query - * @deprecated - */ - public SqlFutureList findFutureList(); - - /** - * The same as bind for named parameters. - */ - public SqlQuery setParameter(String name, Object value); - - /** - * The same as bind for positioned parameters. - */ - public SqlQuery setParameter(int position, Object value); - - /** - * Set a listener to process the query on a row by row basis. - *

    - * It this case the rows are not loaded into the persistence context and - * instead can be processed by the query listener. - *

    - *

    - * Use this when you want to process a large query and do not want to hold the - * entire query result in memory. - *

    - */ - public SqlQuery setListener(SqlQueryListener queryListener); - - /** - * Set the index of the first row of the results to return. - */ - public SqlQuery setFirstRow(int firstRow); - - /** - * Set the maximum number of query results to return. - */ - public SqlQuery setMaxRows(int maxRows); - - /** - * Set the index after which fetching continues in a background thread. - */ - public SqlQuery setBackgroundFetchAfter(int backgroundFetchAfter); - - /** - * Set the column to use to determine the keys for a Map. - */ - public SqlQuery setMapKey(String mapKey); - - /** - * Set a timeout on this query. - *

    - * This will typically result in a call to setQueryTimeout() on a - * 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. - */ - public SqlQuery setTimeout(int secs); - - /** - * A hint which for JDBC translates to the Statement.fetchSize(). - *

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

    - */ - public SqlQuery setBufferFetchSizeHint(int bufferFetchSizeHint); - -} +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. + *

    + * Firstly note that you can use your own sql queries with entity beans + * by using the SqlSelect annotation. This should be your first approach when + * wanting to use your own SQL queries. + *

    + *

    + * If ORM Mapping is too tight and constraining for your problem then SqlQuery + * could be a good approach. + *

    + *

    + * The returned SqlRow objects are similar to a LinkedHashMap with some type + * conversion support added. + *

    + * + *
    + * // its typically a good idea to use a named query
    + * // and put the sql in the orm.xml instead of in your code
    + * 
    + * String sql = "select id, name from customer where name like :name and status_code = :status";
    + * 
    + * SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
    + * sqlQuery.setParameter("name", "Acme%");
    + * sqlQuery.setParameter("status", "ACTIVE");
    + * 
    + * // execute the query returning a List of MapBean objects
    + * List<SqlRow> list = sqlQuery.findList();
    + * 
    + * + */ +public interface SqlQuery extends Serializable { + + /** + * Cancel the query if support by the underlying database and driver. + *

    + * This must be called from a different thread to the one executing the query. + *

    + */ + void cancel(); + + /** + * Execute the query returning a list. + */ + List findList(); + + /** + * Execute the query returning a set. + */ + Set findSet(); + + /** + * Execute the query returning a map. + */ + Map findMap(); + + /** + * Execute the query returning a single row or null. + *

    + * If this query finds 2 or more rows then it will throw a + * PersistenceException. + *

    + */ + SqlRow findUnique(); + + /** + * Execute find list SQL 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 list result of the query + * @deprecated + */ + SqlFutureList findFutureList(); + + /** + * The same as bind for named parameters. + */ + SqlQuery setParameter(String name, Object value); + + /** + * The same as bind for positioned parameters. + */ + SqlQuery setParameter(int position, Object value); + + /** + * Set a listener to process the query on a row by row basis. + *

    + * It this case the rows are not loaded into the persistence context and + * instead can be processed by the query listener. + *

    + *

    + * Use this when you want to process a large query and do not want to hold the + * entire query result in memory. + *

    + */ + SqlQuery setListener(SqlQueryListener queryListener); + + /** + * Set the index of the first row of the results to return. + */ + SqlQuery setFirstRow(int firstRow); + + /** + * Set the maximum number of query results to return. + */ + SqlQuery setMaxRows(int maxRows); + + /** + * Set the index after which fetching continues in a background thread. + */ + SqlQuery setBackgroundFetchAfter(int backgroundFetchAfter); + + /** + * Set the column to use to determine the keys for a Map. + */ + SqlQuery setMapKey(String mapKey); + + /** + * Set a timeout on this query. + *

    + * This will typically result in a call to setQueryTimeout() on a + * 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. + */ + SqlQuery setTimeout(int secs); + + /** + * A hint which for JDBC translates to the Statement.fetchSize(). + *

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

    + */ + SqlQuery setBufferFetchSizeHint(int bufferFetchSizeHint); + +} diff --git a/src/main/java/com/avaje/ebean/SqlQueryListener.java b/src/main/java/com/avaje/ebean/SqlQueryListener.java index b6c50d1a7..411cd27e2 100644 --- a/src/main/java/com/avaje/ebean/SqlQueryListener.java +++ b/src/main/java/com/avaje/ebean/SqlQueryListener.java @@ -1,33 +1,33 @@ -package com.avaje.ebean; - -/** - * Provides a mechanism for processing a SqlQuery one SqlRow at a time. - *

    - * This is useful when the query will return a large number of results and you - * want to process the beans one at a time rather than have all of the beans in - * memory at once. - *

    - * - *
    - * SqlQueryListener listener = ...;
    - *    
    - * SqlQuery query  = Ebean.createSqlQuery("my.large.query");
    - *    
    - * // set the listener that will process each row one at a time
    - * query.setListener(listener);
    - *    
    - * // execute the query. Note that the returned
    - * // list will be empty ... so don't bother assigning it...
    - * query.findList();
    - * 
    - */ -public interface SqlQueryListener { - - /** - * Process the bean that has just been read. - *

    - * Note this bean will not be added to the List Set or Map. - *

    - */ - public void process(SqlRow bean); -} +package com.avaje.ebean; + +/** + * Provides a mechanism for processing a SqlQuery one SqlRow at a time. + *

    + * This is useful when the query will return a large number of results and you + * want to process the beans one at a time rather than have all of the beans in + * memory at once. + *

    + * + *
    + * SqlQueryListener listener = ...;
    + *    
    + * SqlQuery query  = Ebean.createSqlQuery("my.large.query");
    + *    
    + * // set the listener that will process each row one at a time
    + * query.setListener(listener);
    + *    
    + * // execute the query. Note that the returned
    + * // list will be empty ... so don't bother assigning it...
    + * query.findList();
    + * 
    + */ +public interface SqlQueryListener { + + /** + * Process the bean that has just been read. + *

    + * Note this bean will not be added to the List Set or Map. + *

    + */ + void process(SqlRow bean); +} diff --git a/src/main/java/com/avaje/ebean/SqlRow.java b/src/main/java/com/avaje/ebean/SqlRow.java index 9c279a3a8..3fc483212 100644 --- a/src/main/java/com/avaje/ebean/SqlRow.java +++ b/src/main/java/com/avaje/ebean/SqlRow.java @@ -1,168 +1,168 @@ -package com.avaje.ebean; - -import java.io.Serializable; -import java.math.BigDecimal; -import java.sql.Date; -import java.sql.Timestamp; -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.UUID; - -/** - * Used to return raw SQL query results. - *

    - * Refer to {@link SqlQuery} for examples. - *

    - *

    - * There are convenience methods such as getInteger(), getBigDecimal() etc. The - * reason for these methods is that the values put into this map often come - * straight from the JDBC resultSet. Depending on the JDBC driver it may put a - * different type into a given property. For example an Integer, BigDecimal, - * Double could all be put into a property depending on the JDBC driver used. - * These convenience methods automatically convert the value as required - * returning the type you expect. - *

    - */ -public interface SqlRow extends Serializable, Map { - - /** - * Return the property names (String). - *

    - * Internally this uses LinkedHashMap and so the order of the property names - * should be predictable and ordered by the use of LinkedHashMap. - *

    - */ - public Iterator keys(); - - /** - * Remove a property from the map. Returns the value of the removed property. - */ - public Object remove(Object name); - - /** - * Return a property value by its name. - */ - public Object get(Object name); - - /** - * Set a value to a property. - */ - public Object put(String name, Object value); - - /** - * Exactly the same as the put method. - *

    - * I added this method because it seems more bean like to have get and set - * methods. - *

    - */ - public Object set(String name, Object value); - - /** - * Return a property as a Boolean. - */ - public Boolean getBoolean(String name); - - /** - * Return a property as a UUID. - */ - public UUID getUUID(String name); - - /** - * Return a property as an Integer. - */ - public Integer getInteger(String name); - - /** - * Return a property value as a BigDecimal. - */ - public BigDecimal getBigDecimal(String name); - - /** - * Return a property value as a Long. - */ - public Long getLong(String name); - - /** - * Return the property value as a Double. - */ - public Double getDouble(String name); - - /** - * Return the property value as a Float. - */ - public Float getFloat(String name); - - /** - * Return a property as a String. - */ - public String getString(String name); - - /** - * Return the property as a java.util.Date. - */ - public java.util.Date getUtilDate(String name); - - /** - * Return the property as a sql date. - */ - public Date getDate(String name); - - /** - * Return the property as a sql timestamp. - */ - public Timestamp getTimestamp(String name); - - /** - * String description of the underlying map. - */ - public String toString(); - - /** - * Clear the map. - */ - public void clear(); - - /** - * Returns true if the map contains the property. - */ - public boolean containsKey(Object key); - - /** - * Returns true if the map contains the value. - */ - public boolean containsValue(Object value); - - /** - * Returns the entrySet of the map. - */ - public Set> entrySet(); - - /** - * Returns true if the map is empty. - */ - public boolean isEmpty(); - - /** - * Returns the key set of the map. - */ - public Set keySet(); - - /** - * Put all the values from t into this map. - */ - public void putAll(Map t); - - /** - * Return the size of the map. - */ - public int size(); - - /** - * Return the values from this map. - */ - public Collection values(); - +package com.avaje.ebean; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.sql.Date; +import java.sql.Timestamp; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +/** + * Used to return raw SQL query results. + *

    + * Refer to {@link SqlQuery} for examples. + *

    + *

    + * There are convenience methods such as getInteger(), getBigDecimal() etc. The + * reason for these methods is that the values put into this map often come + * straight from the JDBC resultSet. Depending on the JDBC driver it may put a + * different type into a given property. For example an Integer, BigDecimal, + * Double could all be put into a property depending on the JDBC driver used. + * These convenience methods automatically convert the value as required + * returning the type you expect. + *

    + */ +public interface SqlRow extends Serializable, Map { + + /** + * Return the property names (String). + *

    + * Internally this uses LinkedHashMap and so the order of the property names + * should be predictable and ordered by the use of LinkedHashMap. + *

    + */ + Iterator keys(); + + /** + * Remove a property from the map. Returns the value of the removed property. + */ + Object remove(Object name); + + /** + * Return a property value by its name. + */ + Object get(Object name); + + /** + * Set a value to a property. + */ + Object put(String name, Object value); + + /** + * Exactly the same as the put method. + *

    + * I added this method because it seems more bean like to have get and set + * methods. + *

    + */ + Object set(String name, Object value); + + /** + * Return a property as a Boolean. + */ + Boolean getBoolean(String name); + + /** + * Return a property as a UUID. + */ + UUID getUUID(String name); + + /** + * Return a property as an Integer. + */ + Integer getInteger(String name); + + /** + * Return a property value as a BigDecimal. + */ + BigDecimal getBigDecimal(String name); + + /** + * Return a property value as a Long. + */ + Long getLong(String name); + + /** + * Return the property value as a Double. + */ + Double getDouble(String name); + + /** + * Return the property value as a Float. + */ + Float getFloat(String name); + + /** + * Return a property as a String. + */ + String getString(String name); + + /** + * Return the property as a java.util.Date. + */ + java.util.Date getUtilDate(String name); + + /** + * Return the property as a sql date. + */ + Date getDate(String name); + + /** + * Return the property as a sql timestamp. + */ + Timestamp getTimestamp(String name); + + /** + * String description of the underlying map. + */ + String toString(); + + /** + * Clear the map. + */ + void clear(); + + /** + * Returns true if the map contains the property. + */ + boolean containsKey(Object key); + + /** + * Returns true if the map contains the value. + */ + boolean containsValue(Object value); + + /** + * Returns the entrySet of the map. + */ + Set> entrySet(); + + /** + * Returns true if the map is empty. + */ + boolean isEmpty(); + + /** + * Returns the key set of the map. + */ + Set keySet(); + + /** + * Put all the values from t into this map. + */ + void putAll(Map t); + + /** + * Return the size of the map. + */ + int size(); + + /** + * Return the values from this map. + */ + Collection values(); + } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/SqlUpdate.java b/src/main/java/com/avaje/ebean/SqlUpdate.java index 716478070..25eda5bdc 100644 --- a/src/main/java/com/avaje/ebean/SqlUpdate.java +++ b/src/main/java/com/avaje/ebean/SqlUpdate.java @@ -1,146 +1,146 @@ -package com.avaje.ebean; - -/** - * A SqlUpdate for executing insert update or delete statements. - *

    - * Provides a simple way to execute raw SQL insert update or delete statements - * without having to resort to JDBC. - *

    - *

    - * Supports the use of positioned or named parameters and can automatically - * notify Ebean of the table modified so that Ebean can maintain its cache. - *

    - *

    - * Note that {@link #setAutoTableMod(boolean)} and - * Ebean#externalModification(String, boolean, boolean, boolean)} can be to - * notify Ebean of external changes and enable Ebean to maintain it's "L2" - * server cache. - *

    - * - *
    - * // example that uses 'named' parameters 
    - * String s = "UPDATE f_topic set post_count = :count where id = :id"
    - * SqlUpdate update = Ebean.createSqlUpdate(s);
    - * update.setParameter("id", 1);
    - * update.setParameter("count", 50);
    - * 
    - * int modifiedCount = Ebean.execute(update);
    - * 
    - * String msg = "There where " + modifiedCount + "rows updated"
    - * 
    - * - * @see Update - * @see SqlQuery - * @see CallableSql - */ -public interface SqlUpdate { - - /** - * Execute the update returning the number of rows modified. - *

    - * After you have executed the SqlUpdate you can bind new variables using - * {@link #setParameter(String, Object)} etc and then execute the SqlUpdate - * again. - *

    - *

    - * For JDBC batch processing refer to - * {@link Transaction#setBatchMode(boolean)} and - * {@link Transaction#setBatchSize(int)}. - *

    - * - * @see com.avaje.ebean.Ebean#execute(SqlUpdate) - */ - public int execute(); - - /** - * Return true if eBean should automatically deduce the table modification - * information and process it. - *

    - * If this is true then cache invalidation and text index management are aware - * of the modification. - *

    - */ - public boolean isAutoTableMod(); - - /** - * Set this to false if you don't want eBean to automatically deduce the table - * modification information and process it. - *

    - * Set this to false if you don't want any cache invalidation or text index - * management to occur. You may do this when say you update only one column - * and you know that it is not important for cached objects or text indexes. - *

    - */ - public SqlUpdate setAutoTableMod(boolean isAutoTableMod); - - /** - * Return the label that can be seen in the transaction logs. - */ - public String getLabel(); - - /** - * Set a descriptive text that can be put into the transaction log. - *

    - * Useful when identifying the statement in the transaction log. - *

    - */ - public SqlUpdate setLabel(String label); - - /** - * Return the sql statement. - */ - public String getSql(); - - /** - * Return the generated sql that has named parameters converted to positioned parameters. - */ - public String getGeneratedSql(); - - /** - * Return the timeout used to execute this statement. - */ - public int getTimeout(); - - /** - * Set the timeout in seconds. Zero implies no limit. - *

    - * This will set the query timeout on the underlying PreparedStatement. If the - * timeout expires a SQLException will be throw and wrapped in a - * PersistenceException. - *

    - */ - public SqlUpdate setTimeout(int secs); - - /** - * Set a parameter via its index position. - */ - public SqlUpdate setParameter(int position, Object value); - - /** - * Set a null parameter via its index position. Exactly the same as - * {@link #setNull(int, int)}. - */ - public SqlUpdate setNull(int position, int jdbcType); - - /** - * Set a null valued parameter using its index position. - */ - public SqlUpdate setNullParameter(int position, int jdbcType); - - /** - * Set a named parameter value. - */ - public SqlUpdate setParameter(String name, Object param); - - /** - * Set a named parameter that has a null value. Exactly the same as - * {@link #setNullParameter(String, int)}. - */ - public SqlUpdate setNull(String name, int jdbcType); - - /** - * Set a named parameter that has a null value. - */ - public SqlUpdate setNullParameter(String name, int jdbcType); - +package com.avaje.ebean; + +/** + * A SqlUpdate for executing insert update or delete statements. + *

    + * Provides a simple way to execute raw SQL insert update or delete statements + * without having to resort to JDBC. + *

    + *

    + * Supports the use of positioned or named parameters and can automatically + * notify Ebean of the table modified so that Ebean can maintain its cache. + *

    + *

    + * Note that {@link #setAutoTableMod(boolean)} and + * Ebean#externalModification(String, boolean, boolean, boolean)} can be to + * notify Ebean of external changes and enable Ebean to maintain it's "L2" + * server cache. + *

    + * + *
    + * // example that uses 'named' parameters 
    + * String s = "UPDATE f_topic set post_count = :count where id = :id"
    + * SqlUpdate update = Ebean.createSqlUpdate(s);
    + * update.setParameter("id", 1);
    + * update.setParameter("count", 50);
    + * 
    + * int modifiedCount = Ebean.execute(update);
    + * 
    + * String msg = "There where " + modifiedCount + "rows updated"
    + * 
    + * + * @see Update + * @see SqlQuery + * @see CallableSql + */ +public interface SqlUpdate { + + /** + * Execute the update returning the number of rows modified. + *

    + * After you have executed the SqlUpdate you can bind new variables using + * {@link #setParameter(String, Object)} etc and then execute the SqlUpdate + * again. + *

    + *

    + * For JDBC batch processing refer to + * {@link Transaction#setBatchMode(boolean)} and + * {@link Transaction#setBatchSize(int)}. + *

    + * + * @see com.avaje.ebean.Ebean#execute(SqlUpdate) + */ + int execute(); + + /** + * Return true if eBean should automatically deduce the table modification + * information and process it. + *

    + * If this is true then cache invalidation and text index management are aware + * of the modification. + *

    + */ + boolean isAutoTableMod(); + + /** + * Set this to false if you don't want eBean to automatically deduce the table + * modification information and process it. + *

    + * Set this to false if you don't want any cache invalidation or text index + * management to occur. You may do this when say you update only one column + * and you know that it is not important for cached objects or text indexes. + *

    + */ + SqlUpdate setAutoTableMod(boolean isAutoTableMod); + + /** + * Return the label that can be seen in the transaction logs. + */ + String getLabel(); + + /** + * Set a descriptive text that can be put into the transaction log. + *

    + * Useful when identifying the statement in the transaction log. + *

    + */ + SqlUpdate setLabel(String label); + + /** + * Return the sql statement. + */ + String getSql(); + + /** + * Return the generated sql that has named parameters converted to positioned parameters. + */ + String getGeneratedSql(); + + /** + * Return the timeout used to execute this statement. + */ + int getTimeout(); + + /** + * Set the timeout in seconds. Zero implies no limit. + *

    + * This will set the query timeout on the underlying PreparedStatement. If the + * timeout expires a SQLException will be throw and wrapped in a + * PersistenceException. + *

    + */ + SqlUpdate setTimeout(int secs); + + /** + * Set a parameter via its index position. + */ + SqlUpdate setParameter(int position, Object value); + + /** + * Set a null parameter via its index position. Exactly the same as + * {@link #setNull(int, int)}. + */ + SqlUpdate setNull(int position, int jdbcType); + + /** + * Set a null valued parameter using its index position. + */ + SqlUpdate setNullParameter(int position, int jdbcType); + + /** + * Set a named parameter value. + */ + SqlUpdate setParameter(String name, Object param); + + /** + * Set a named parameter that has a null value. Exactly the same as + * {@link #setNullParameter(String, int)}. + */ + SqlUpdate setNull(String name, int jdbcType); + + /** + * Set a named parameter that has a null value. + */ + SqlUpdate setNullParameter(String name, int jdbcType); + } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/Transaction.java b/src/main/java/com/avaje/ebean/Transaction.java index c61dbad0b..9a8324297 100644 --- a/src/main/java/com/avaje/ebean/Transaction.java +++ b/src/main/java/com/avaje/ebean/Transaction.java @@ -1,329 +1,329 @@ -package com.avaje.ebean; - -import com.avaje.ebean.config.PersistBatch; - -import javax.persistence.OptimisticLockException; -import javax.persistence.PersistenceException; -import javax.persistence.RollbackException; -import java.io.Closeable; -import java.sql.Connection; - -/** - * The Transaction object. Typically representing a JDBC or JTA transaction. - */ -public interface Transaction extends Closeable { - - /** - * Read Committed transaction isolation. Same as - * java.sql.Connection.TRANSACTION_READ_COMMITTED. - */ - public static final int READ_COMMITTED = java.sql.Connection.TRANSACTION_READ_COMMITTED; - - /** - * Read Uncommitted transaction isolation. Same as - * java.sql.Connection.TRANSACTION_READ_UNCOMMITTED. - */ - public static final int READ_UNCOMMITTED = java.sql.Connection.TRANSACTION_READ_UNCOMMITTED; - - /** - * Repeatable read transaction isolation. Same as - * java.sql.Connection.TRANSACTION_REPEATABLE_READ. - */ - public static final int REPEATABLE_READ = java.sql.Connection.TRANSACTION_REPEATABLE_READ; - - /** - * Serializable transaction isolation. Same as - * java.sql.Connection.TRANSACTION_SERIALIZABLE. - */ - public static final int SERIALIZABLE = java.sql.Connection.TRANSACTION_SERIALIZABLE; - - /** - * Register a TransactionCallback with this transaction. - */ - public void register(TransactionCallback callback); - - /** - * Return true if this transaction is read only. - */ - public boolean isReadOnly(); - - /** - * Set whether this transaction should be readOnly. - */ - public void setReadOnly(boolean readOnly); - - /** - * Commit the transaction. - */ - public void commit() throws RollbackException; - - /** - * Rollback the transaction. - */ - public void rollback() throws PersistenceException; - - /** - * Rollback the transaction specifying a throwable that caused the rollback to - * occur. - *

    - * If you are using transaction logging this will log the throwable in the - * transaction logs. - *

    - */ - public void rollback(Throwable e) throws PersistenceException; - - /** - * If the transaction is active then perform rollback. Otherwise do nothing. - */ - public void end() throws PersistenceException; - - /** - * Return true if the transaction is active. - */ - public boolean isActive(); - - /** - * Explicitly turn off or on the cascading nature of save() and delete(). This - * gives the developer exact control over what beans are saved and deleted - * rather than Ebean cascading detecting 'dirty/modified' beans etc. - *

    - * This is useful if you can getting back entity beans from a layer of code - * (potentially remote) and you prefer to have exact control. - *

    - *

    - * This may also be useful if you are using jdbc batching with jdbc drivers - * that do not support getGeneratedKeys. - *

    - */ - public void setPersistCascade(boolean persistCascade); - - /** - * Turn on or off statement batching. Statement batching can be transparent - * for drivers and databases that support getGeneratedKeys. Otherwise you may - * wish to specifically control when batching is used via this method. - *

    - * Refer to java.sql.PreparedStatement.addBatch(); - *

    - * Note that you may also wish to use the setPersistCascade method to stop - * save and delete cascade behaviour. You may do this to have full control - * over the order of execution rather than the normal cascading fashion. - *

    - *

    - * Note that the execution order in batch mode may be different from - * non batch mode execution order. Also note that insert behaviour - * may be different depending on the JDBC driver and its support for - * getGeneratedKeys. That is, for JDBC drivers that do not support - * getGeneratedKeys you may not get back the generated IDs (used for inserting - * associated detail beans etc). - *

    - *

    - * Calls to save(), delete(), insert() and execute() all support batch - * processing. This includes normal beans, MapBean, CallableSql and UpdateSql. - *

    - *

    - * The flushing of the batched statements is automatic but you can call - * batchFlush when you like. Note that flushing occurs when a query is - * executed or when you mix UpdateSql and CallableSql with save and delete of - * beans. - *

    - *

    - * Example: batch processing executing every 3 rows - *

    - * - *
    {@code
    -   *
    -   * String data = "This is a simple test of the batch processing"
    -   *             + " mode and the transaction execute batch method";
    -   * 
    -   * String[] da = data.split(" ");
    -   * 
    -   * String sql = "{call sp_t3(?,?)}";
    -   * 
    -   * CallableSql cs = new CallableSql(sql);
    -   * cs.registerOut(2, Types.INTEGER);
    -   * 
    -   * // (optional) inform eBean this stored procedure
    -   * // inserts into a table called sp_test
    -   * cs.addModification("sp_test", true, false, false);
    -   * 
    -   * Transaction txn = ebeanServer.beginTransaction();
    -   * txn.setBatchMode(true);
    -   * txn.setBatchSize(3);
    -   * try {
    -   *   for (int i = 0; i < da.length;) {
    -   *     cs.setParameter(1, da[i]);
    -   *     ebeanServer.execute(cs);
    -   *   }
    -   * 
    -   *   // NB: commit implicitly flushes
    -   *   txn.commit();
    -   * 
    -   * } finally {
    -   *   txn.end();
    -   * }
    -   *
    -   * }
    - * - */ - public void setBatchMode(boolean useBatch); - - /** - * The JDBC batch mode to use for this transaction. - *

    - * If this is NONE then JDBC batch can still be used for each request - save(), insert(), update() or delete() - * and this would be useful if the request cascades to detail beans. - *

    - * - * @param persistBatchMode the batch mode to use for this transaction - * - * @see com.avaje.ebean.config.ServerConfig#setPersistBatch(com.avaje.ebean.config.PersistBatch) - */ - public void setBatch(PersistBatch persistBatchMode); - - /** - * Return the batch mode at the transaction level. - */ - public PersistBatch getBatch(); - - /** - * Set the JDBC batch mode to use for a save() or delete() request. - *

    - * This only takes effect when batch mode on the transaction has not already meant that - * JDBC batch mode is being used. - *

    - *

    - * This is useful when the single save() or delete() cascades. For example, inserting a 'master' cascades - * and inserts a collection of 'detail' beans. The detail beans can be inserted using JDBC batch. - *

    - * - * @param batchOnCascadeMode the batch mode to use per save(), insert(), update() or delete() - * - * @see com.avaje.ebean.config.ServerConfig#setPersistBatchOnCascade(com.avaje.ebean.config.PersistBatch) - */ - public void setBatchOnCascade(PersistBatch batchOnCascadeMode); - - /** - * Return the batch mode at the request level (for each save(), insert(), update() or delete()). - */ - public PersistBatch getBatchOnCascade(); - - /** - * Specify the number of statements before a batch is flushed automatically. - */ - public void setBatchSize(int batchSize); - - /** - * Return the current batch size. - */ - public int getBatchSize(); - - /** - * Specify if you want batched inserts to use getGeneratedKeys. - *

    - * By default batched inserts will try to use getGeneratedKeys if it is - * supported by the underlying jdbc driver and database. - *

    - *

    - * You may want to turn getGeneratedKeys off when you are inserting a large - * number of objects and you don't care about getting back the ids. - *

    - */ - public void setBatchGetGeneratedKeys(boolean getGeneratedKeys); - - /** - * By default when mixing UpdateSql (or CallableSql) with Beans the batch is - * automatically flushed when you change (between persisting beans and - * executing UpdateSql or CallableSql). - *

    - * If you want to execute both WITHOUT having the batch automatically flush - * you need to call this with batchFlushOnMixed = false. - *

    - *

    - * Note that UpdateSql and CallableSql are ALWAYS executed first (before the - * beans are executed). This is because the UpdateSql and CallableSql have - * already been bound to their PreparedStatements. The beans on the other hand - * have a 2 step process (delayed binding). - *

    - */ - public void setBatchFlushOnMixed(boolean batchFlushOnMixed); - - /** - * By default executing a query will automatically flush any batched - * statements (persisted beans, executed UpdateSql etc). - *

    - * Calling this method with batchFlushOnQuery = false means that you can - * execute a query and the batch will not be automatically flushed. - *

    - */ - public void setBatchFlushOnQuery(boolean batchFlushOnQuery); - - /** - * Return true if the batch (of persisted beans or executed UpdateSql etc) - * should be flushed prior to executing a query. - *

    - * The default is for this to be true. - *

    - */ - public boolean isBatchFlushOnQuery(); - - /** - * The batch will be flushing automatically but you can use this to explicitly - * flush the batch if you like. - *

    - * Flushing occurs automatically when: - *

    - *
      - *
    • the batch size is reached
    • - *
    • A query is executed on the same transaction
    • - *
    • UpdateSql or CallableSql are mixed with bean save and delete
    • - *
    • Transaction commit occurs
    • - *
    - */ - public void flushBatch() throws PersistenceException, OptimisticLockException; - - /** - * Return the underlying Connection object. - *

    - * Useful where a Developer wishes to use the JDBC API directly. Note that the - * commit() rollback() and end() methods on the Transaction should still be - * used. Calling these methods on the Connection would be a big no no unless - * you know what you are doing. - *

    - *

    - * Examples of when a developer may wish to use the connection directly are: - * Savepoints, advanced CLOB BLOB use and advanced stored procedure calls. - *

    - */ - public Connection getConnection(); - - /** - * Add table modification information to the TransactionEvent. - *

    - * Use this in conjunction with getConnection() and raw JDBC. - *

    - *

    - * This effectively informs Ebean of the data that has been changed by the - * transaction and this information is normally automatically handled by Ebean - * when you save entity beans or use UpdateSql etc. - *

    - *

    - * If you use raw JDBC then you can use this method to inform Ebean for the - * tables that have been modified. Ebean uses this information to keep its - * caches in synch and maintain text indexes. - *

    - */ - public void addModification(String tableName, boolean inserts, boolean updates, boolean deletes); - - /** - * 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}. - */ - public void putUserObject(String name, Object value); - - /** - * Get an object added with {@link #putUserObject(String, Object)}. - */ - public Object getUserObject(String name); -} +package com.avaje.ebean; + +import com.avaje.ebean.config.PersistBatch; + +import javax.persistence.OptimisticLockException; +import javax.persistence.PersistenceException; +import javax.persistence.RollbackException; +import java.io.Closeable; +import java.sql.Connection; + +/** + * The Transaction object. Typically representing a JDBC or JTA transaction. + */ +public interface Transaction extends Closeable { + + /** + * Read Committed transaction isolation. Same as + * java.sql.Connection.TRANSACTION_READ_COMMITTED. + */ + static final int READ_COMMITTED = java.sql.Connection.TRANSACTION_READ_COMMITTED; + + /** + * Read Uncommitted transaction isolation. Same as + * java.sql.Connection.TRANSACTION_READ_UNCOMMITTED. + */ + static final int READ_UNCOMMITTED = java.sql.Connection.TRANSACTION_READ_UNCOMMITTED; + + /** + * Repeatable read transaction isolation. Same as + * java.sql.Connection.TRANSACTION_REPEATABLE_READ. + */ + static final int REPEATABLE_READ = java.sql.Connection.TRANSACTION_REPEATABLE_READ; + + /** + * Serializable transaction isolation. Same as + * java.sql.Connection.TRANSACTION_SERIALIZABLE. + */ + static final int SERIALIZABLE = java.sql.Connection.TRANSACTION_SERIALIZABLE; + + /** + * Register a TransactionCallback with this transaction. + */ + void register(TransactionCallback callback); + + /** + * Return true if this transaction is read only. + */ + boolean isReadOnly(); + + /** + * Set whether this transaction should be readOnly. + */ + void setReadOnly(boolean readOnly); + + /** + * Commit the transaction. + */ + void commit() throws RollbackException; + + /** + * Rollback the transaction. + */ + void rollback() throws PersistenceException; + + /** + * Rollback the transaction specifying a throwable that caused the rollback to + * occur. + *

    + * If you are using transaction logging this will log the throwable in the + * transaction logs. + *

    + */ + void rollback(Throwable e) throws PersistenceException; + + /** + * If the transaction is active then perform rollback. Otherwise do nothing. + */ + void end() throws PersistenceException; + + /** + * Return true if the transaction is active. + */ + boolean isActive(); + + /** + * Explicitly turn off or on the cascading nature of save() and delete(). This + * gives the developer exact control over what beans are saved and deleted + * rather than Ebean cascading detecting 'dirty/modified' beans etc. + *

    + * This is useful if you can getting back entity beans from a layer of code + * (potentially remote) and you prefer to have exact control. + *

    + *

    + * This may also be useful if you are using jdbc batching with jdbc drivers + * that do not support getGeneratedKeys. + *

    + */ + void setPersistCascade(boolean persistCascade); + + /** + * Turn on or off statement batching. Statement batching can be transparent + * for drivers and databases that support getGeneratedKeys. Otherwise you may + * wish to specifically control when batching is used via this method. + *

    + * Refer to java.sql.PreparedStatement.addBatch(); + *

    + * Note that you may also wish to use the setPersistCascade method to stop + * save and delete cascade behaviour. You may do this to have full control + * over the order of execution rather than the normal cascading fashion. + *

    + *

    + * Note that the execution order in batch mode may be different from + * non batch mode execution order. Also note that insert behaviour + * may be different depending on the JDBC driver and its support for + * getGeneratedKeys. That is, for JDBC drivers that do not support + * getGeneratedKeys you may not get back the generated IDs (used for inserting + * associated detail beans etc). + *

    + *

    + * Calls to save(), delete(), insert() and execute() all support batch + * processing. This includes normal beans, MapBean, CallableSql and UpdateSql. + *

    + *

    + * The flushing of the batched statements is automatic but you can call + * batchFlush when you like. Note that flushing occurs when a query is + * executed or when you mix UpdateSql and CallableSql with save and delete of + * beans. + *

    + *

    + * Example: batch processing executing every 3 rows + *

    + * + *
    {@code
    +   *
    +   * String data = "This is a simple test of the batch processing"
    +   *             + " mode and the transaction execute batch method";
    +   * 
    +   * String[] da = data.split(" ");
    +   * 
    +   * String sql = "{call sp_t3(?,?)}";
    +   * 
    +   * CallableSql cs = new CallableSql(sql);
    +   * cs.registerOut(2, Types.INTEGER);
    +   * 
    +   * // (optional) inform eBean this stored procedure
    +   * // inserts into a table called sp_test
    +   * cs.addModification("sp_test", true, false, false);
    +   * 
    +   * Transaction txn = ebeanServer.beginTransaction();
    +   * txn.setBatchMode(true);
    +   * txn.setBatchSize(3);
    +   * try {
    +   *   for (int i = 0; i < da.length;) {
    +   *     cs.setParameter(1, da[i]);
    +   *     ebeanServer.execute(cs);
    +   *   }
    +   * 
    +   *   // NB: commit implicitly flushes
    +   *   txn.commit();
    +   * 
    +   * } finally {
    +   *   txn.end();
    +   * }
    +   *
    +   * }
    + * + */ + void setBatchMode(boolean useBatch); + + /** + * The JDBC batch mode to use for this transaction. + *

    + * If this is NONE then JDBC batch can still be used for each request - save(), insert(), update() or delete() + * and this would be useful if the request cascades to detail beans. + *

    + * + * @param persistBatchMode the batch mode to use for this transaction + * + * @see com.avaje.ebean.config.ServerConfig#setPersistBatch(com.avaje.ebean.config.PersistBatch) + */ + void setBatch(PersistBatch persistBatchMode); + + /** + * Return the batch mode at the transaction level. + */ + PersistBatch getBatch(); + + /** + * Set the JDBC batch mode to use for a save() or delete() request. + *

    + * This only takes effect when batch mode on the transaction has not already meant that + * JDBC batch mode is being used. + *

    + *

    + * This is useful when the single save() or delete() cascades. For example, inserting a 'master' cascades + * and inserts a collection of 'detail' beans. The detail beans can be inserted using JDBC batch. + *

    + * + * @param batchOnCascadeMode the batch mode to use per save(), insert(), update() or delete() + * + * @see com.avaje.ebean.config.ServerConfig#setPersistBatchOnCascade(com.avaje.ebean.config.PersistBatch) + */ + void setBatchOnCascade(PersistBatch batchOnCascadeMode); + + /** + * Return the batch mode at the request level (for each save(), insert(), update() or delete()). + */ + PersistBatch getBatchOnCascade(); + + /** + * Specify the number of statements before a batch is flushed automatically. + */ + void setBatchSize(int batchSize); + + /** + * Return the current batch size. + */ + int getBatchSize(); + + /** + * Specify if you want batched inserts to use getGeneratedKeys. + *

    + * By default batched inserts will try to use getGeneratedKeys if it is + * supported by the underlying jdbc driver and database. + *

    + *

    + * You may want to turn getGeneratedKeys off when you are inserting a large + * number of objects and you don't care about getting back the ids. + *

    + */ + void setBatchGetGeneratedKeys(boolean getGeneratedKeys); + + /** + * By default when mixing UpdateSql (or CallableSql) with Beans the batch is + * automatically flushed when you change (between persisting beans and + * executing UpdateSql or CallableSql). + *

    + * If you want to execute both WITHOUT having the batch automatically flush + * you need to call this with batchFlushOnMixed = false. + *

    + *

    + * Note that UpdateSql and CallableSql are ALWAYS executed first (before the + * beans are executed). This is because the UpdateSql and CallableSql have + * already been bound to their PreparedStatements. The beans on the other hand + * have a 2 step process (delayed binding). + *

    + */ + void setBatchFlushOnMixed(boolean batchFlushOnMixed); + + /** + * By default executing a query will automatically flush any batched + * statements (persisted beans, executed UpdateSql etc). + *

    + * Calling this method with batchFlushOnQuery = false means that you can + * execute a query and the batch will not be automatically flushed. + *

    + */ + void setBatchFlushOnQuery(boolean batchFlushOnQuery); + + /** + * Return true if the batch (of persisted beans or executed UpdateSql etc) + * should be flushed prior to executing a query. + *

    + * The default is for this to be true. + *

    + */ + boolean isBatchFlushOnQuery(); + + /** + * The batch will be flushing automatically but you can use this to explicitly + * flush the batch if you like. + *

    + * Flushing occurs automatically when: + *

    + *
      + *
    • the batch size is reached
    • + *
    • A query is executed on the same transaction
    • + *
    • UpdateSql or CallableSql are mixed with bean save and delete
    • + *
    • Transaction commit occurs
    • + *
    + */ + void flushBatch() throws PersistenceException, OptimisticLockException; + + /** + * Return the underlying Connection object. + *

    + * Useful where a Developer wishes to use the JDBC API directly. Note that the + * commit() rollback() and end() methods on the Transaction should still be + * used. Calling these methods on the Connection would be a big no no unless + * you know what you are doing. + *

    + *

    + * Examples of when a developer may wish to use the connection directly are: + * Savepoints, advanced CLOB BLOB use and advanced stored procedure calls. + *

    + */ + Connection getConnection(); + + /** + * Add table modification information to the TransactionEvent. + *

    + * Use this in conjunction with getConnection() and raw JDBC. + *

    + *

    + * This effectively informs Ebean of the data that has been changed by the + * transaction and this information is normally automatically handled by Ebean + * when you save entity beans or use UpdateSql etc. + *

    + *

    + * If you use raw JDBC then you can use this method to inform Ebean for the + * tables that have been modified. Ebean uses this information to keep its + * caches in synch and maintain text indexes. + *

    + */ + void addModification(String tableName, boolean inserts, boolean updates, boolean deletes); + + /** + * 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}. + */ + void putUserObject(String name, Object value); + + /** + * Get an object added with {@link #putUserObject(String, Object)}. + */ + Object getUserObject(String name); +} diff --git a/src/main/java/com/avaje/ebean/TxCallable.java b/src/main/java/com/avaje/ebean/TxCallable.java index 574960958..204b1774a 100644 --- a/src/main/java/com/avaje/ebean/TxCallable.java +++ b/src/main/java/com/avaje/ebean/TxCallable.java @@ -1,45 +1,45 @@ -package com.avaje.ebean; - -/** - * Execute a TxCallable in a Transaction scope. - *

    - * Use this with the {@link Ebean#execute(TxCallable)} method. - *

    - *

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

    - *

    - * See also {@link TxRunnable}. - *

    - * - *
    - * Ebean.execute(new TxCallable<String>() {
    - *   public String call() {
    - *     User u1 = Ebean.find(User.class, 1);
    - *     User u2 = Ebean.find(User.class, 2);
    - * 
    - *     u1.setName("u1 mod");
    - *     u2.setName("u2 mod");
    - * 
    - *     Ebean.save(u1);
    - *     Ebean.save(u2);
    - * 
    - *     return u1.getEmail();
    - *   }
    - * });
    - * 
    - * - * @see TxRunnable - */ -public interface TxCallable { - - /** - * Execute the method within a transaction scope returning the result. - *

    - * If you do not want to return a result you should look to use TxRunnable - * instead. - *

    - */ - public T call(); -} +package com.avaje.ebean; + +/** + * Execute a TxCallable in a Transaction scope. + *

    + * Use this with the {@link Ebean#execute(TxCallable)} method. + *

    + *

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

    + *

    + * See also {@link TxRunnable}. + *

    + * + *
    + * Ebean.execute(new TxCallable<String>() {
    + *   public String call() {
    + *     User u1 = Ebean.find(User.class, 1);
    + *     User u2 = Ebean.find(User.class, 2);
    + * 
    + *     u1.setName("u1 mod");
    + *     u2.setName("u2 mod");
    + * 
    + *     Ebean.save(u1);
    + *     Ebean.save(u2);
    + * 
    + *     return u1.getEmail();
    + *   }
    + * });
    + * 
    + * + * @see TxRunnable + */ +public interface TxCallable { + + /** + * Execute the method within a transaction scope returning the result. + *

    + * If you do not want to return a result you should look to use TxRunnable + * instead. + *

    + */ + T call(); +} diff --git a/src/main/java/com/avaje/ebean/TxIsolation.java b/src/main/java/com/avaje/ebean/TxIsolation.java index 5bd71e6d7..8c0394c29 100644 --- a/src/main/java/com/avaje/ebean/TxIsolation.java +++ b/src/main/java/com/avaje/ebean/TxIsolation.java @@ -1,100 +1,100 @@ -package com.avaje.ebean; - -import java.sql.Connection; - -/** - * The Transaction Isolation levels. - *

    - * These match those of java.sql.Connection with the addition of DEFAULT which - * implies the configured default of the DataSource. - *

    - *

    - * This can be used with TxScope to define transactional scopes to execute - * method within. - *

    - * - * @see TxScope - */ -public enum TxIsolation { - - /** - * Read Committed Isolation level. This is typically the default for most - * configurations. - */ - READ_COMMITED(Connection.TRANSACTION_READ_COMMITTED), - - /** - * Read uncommitted Isolation level. - */ - READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED), - - /** - * Repeatable Read Isolation level. - */ - REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ), - - /** - * Serializable Isolation level. - */ - SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE), - - /** - * No Isolation level. - */ - NONE(Connection.TRANSACTION_NONE), - - /** - * The default isolation level. This typically means the default that the - * DataSource is using or configured to use. - */ - DEFAULT(-1); - - final int level; - - private TxIsolation(int level) { - this.level = level; - } - - /** - * Return the level as per java.sql.Connection. - *

    - * Note that -1 denotes the default isolation level. - *

    - */ - public int getLevel() { - return level; - } - - /** - * Return the TxIsolation given the java.sql.Connection isolation level. - *

    - * Note that -1 denotes the default isolation level. - *

    - */ - public static TxIsolation fromLevel(int connectionIsolationLevel) { - - switch (connectionIsolationLevel) { - case Connection.TRANSACTION_READ_UNCOMMITTED: - return TxIsolation.READ_UNCOMMITTED; - - case Connection.TRANSACTION_READ_COMMITTED: - return TxIsolation.READ_COMMITED; - - case Connection.TRANSACTION_REPEATABLE_READ: - return TxIsolation.REPEATABLE_READ; - - case Connection.TRANSACTION_SERIALIZABLE: - return TxIsolation.SERIALIZABLE; - - case Connection.TRANSACTION_NONE: - return TxIsolation.NONE; - - case -1: - return TxIsolation.DEFAULT; - - default: - throw new RuntimeException("Unknown isolation level " + connectionIsolationLevel); - } - - } -} +package com.avaje.ebean; + +import java.sql.Connection; + +/** + * The Transaction Isolation levels. + *

    + * These match those of java.sql.Connection with the addition of DEFAULT which + * implies the configured default of the DataSource. + *

    + *

    + * This can be used with TxScope to define transactional scopes to execute + * method within. + *

    + * + * @see TxScope + */ +public enum TxIsolation { + + /** + * Read Committed Isolation level. This is typically the default for most + * configurations. + */ + READ_COMMITED(Connection.TRANSACTION_READ_COMMITTED), + + /** + * Read uncommitted Isolation level. + */ + READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED), + + /** + * Repeatable Read Isolation level. + */ + REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ), + + /** + * Serializable Isolation level. + */ + SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE), + + /** + * No Isolation level. + */ + NONE(Connection.TRANSACTION_NONE), + + /** + * The default isolation level. This typically means the default that the + * DataSource is using or configured to use. + */ + DEFAULT(-1); + + final int level; + + TxIsolation(int level) { + this.level = level; + } + + /** + * Return the level as per java.sql.Connection. + *

    + * Note that -1 denotes the default isolation level. + *

    + */ + public int getLevel() { + return level; + } + + /** + * Return the TxIsolation given the java.sql.Connection isolation level. + *

    + * Note that -1 denotes the default isolation level. + *

    + */ + public static TxIsolation fromLevel(int connectionIsolationLevel) { + + switch (connectionIsolationLevel) { + case Connection.TRANSACTION_READ_UNCOMMITTED: + return TxIsolation.READ_UNCOMMITTED; + + case Connection.TRANSACTION_READ_COMMITTED: + return TxIsolation.READ_COMMITED; + + case Connection.TRANSACTION_REPEATABLE_READ: + return TxIsolation.REPEATABLE_READ; + + case Connection.TRANSACTION_SERIALIZABLE: + return TxIsolation.SERIALIZABLE; + + case Connection.TRANSACTION_NONE: + return TxIsolation.NONE; + + case -1: + return TxIsolation.DEFAULT; + + default: + throw new RuntimeException("Unknown isolation level " + connectionIsolationLevel); + } + + } +} diff --git a/src/main/java/com/avaje/ebean/TxRunnable.java b/src/main/java/com/avaje/ebean/TxRunnable.java index 2821882e0..f3bc724cc 100644 --- a/src/main/java/com/avaje/ebean/TxRunnable.java +++ b/src/main/java/com/avaje/ebean/TxRunnable.java @@ -1,39 +1,39 @@ -package com.avaje.ebean; - -/** - * Execute a TxRunnable in a Transaction scope. - *

    - * Use this with the {@link Ebean#execute(TxRunnable)} method. - *

    - *

    - * See also {@link TxCallable}. - *

    - * - *
    - * 
    - * // this run method runs in a transaction scope
    - * // which by default is TxScope.REQUIRED
    - * 
    - * Ebean.execute(new TxRunnable() {
    - *   public void run() {
    - *     User u1 = Ebean.find(User.class, 1);
    - *     User u2 = Ebean.find(User.class, 2);
    - * 
    - *     u1.setName("u1 mod");
    - *     u2.setName("u2 mod");
    - * 
    - *     Ebean.save(u1);
    - *     Ebean.save(u2);
    - *   }
    - * });
    - * 
    - * - * @see TxCallable - */ -public interface TxRunnable { - - /** - * Run the method in a transaction sope. - */ - public void run(); -} +package com.avaje.ebean; + +/** + * Execute a TxRunnable in a Transaction scope. + *

    + * Use this with the {@link Ebean#execute(TxRunnable)} method. + *

    + *

    + * See also {@link TxCallable}. + *

    + * + *
    + * 
    + * // this run method runs in a transaction scope
    + * // which by default is TxScope.REQUIRED
    + * 
    + * Ebean.execute(new TxRunnable() {
    + *   public void run() {
    + *     User u1 = Ebean.find(User.class, 1);
    + *     User u2 = Ebean.find(User.class, 2);
    + * 
    + *     u1.setName("u1 mod");
    + *     u2.setName("u2 mod");
    + * 
    + *     Ebean.save(u1);
    + *     Ebean.save(u2);
    + *   }
    + * });
    + * 
    + * + * @see TxCallable + */ +public interface TxRunnable { + + /** + * Run the method in a transaction sope. + */ + void run(); +} diff --git a/src/main/java/com/avaje/ebean/Update.java b/src/main/java/com/avaje/ebean/Update.java index cb12e1547..d58fb8575 100644 --- a/src/main/java/com/avaje/ebean/Update.java +++ b/src/main/java/com/avaje/ebean/Update.java @@ -54,7 +54,7 @@ public interface Update { /** * Return the name if it is a named update. */ - public String getName(); + String getName(); /** * Set this to false if you do not want the cache to invalidate related @@ -64,7 +64,7 @@ public interface Update { * parts of the "L2" server cache. *

    */ - public Update setNotifyCache(boolean notifyCache); + Update setNotifyCache(boolean notifyCache); /** * Set a timeout for statement execution. @@ -77,12 +77,12 @@ public interface Update { * @param secs * the timeout in seconds. Zero implies unlimited. */ - public Update setTimeout(int secs); + Update setTimeout(int secs); /** * Execute the statement returning the number of rows modified. */ - public int execute(); + int execute(); /** * Set an ordered bind parameter. @@ -98,7 +98,7 @@ public interface Update { * @param value * the parameter value to bind. */ - public Update set(int position, Object value); + Update set(int position, Object value); /** * Set and ordered bind parameter (same as bind). @@ -108,7 +108,7 @@ public interface Update { * @param value * the parameter value to bind. */ - public Update setParameter(int position, Object value); + Update setParameter(int position, Object value); /** * Set an ordered parameter that is null. The JDBC type of the null must be @@ -117,12 +117,12 @@ public interface Update { * position starts at value 1 (not 0) to be consistent with PreparedStatement. *

    */ - public Update setNull(int position, int jdbcType); + Update setNull(int position, int jdbcType); /** * Set an ordered parameter that is null (same as bind). */ - public Update setNullParameter(int position, int jdbcType); + Update setNullParameter(int position, int jdbcType); /** * Set a named parameter. Named parameters have a colon to prefix the name. @@ -135,12 +135,12 @@ public interface Update { * @param value * the parameter value. */ - public Update set(String name, Object value); + Update set(String name, Object value); /** * Bind a named parameter (same as bind). */ - public Update setParameter(String name, Object param); + Update setParameter(String name, Object param); /** * Set a named parameter that is null. The JDBC type of the null must be @@ -154,16 +154,16 @@ public interface Update { * @param jdbcType * the type of the property being bound. */ - public Update setNull(String name, int jdbcType); + Update setNull(String name, int jdbcType); /** * Bind a named parameter that is null (same as bind). */ - public Update setNullParameter(String name, int jdbcType); + Update setNullParameter(String name, int jdbcType); /** * Return the sql that is actually executed. */ - public String getGeneratedSql(); + String getGeneratedSql(); } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/ValuePair.java b/src/main/java/com/avaje/ebean/ValuePair.java index fe11b7799..ec2b6f112 100644 --- a/src/main/java/com/avaje/ebean/ValuePair.java +++ b/src/main/java/com/avaje/ebean/ValuePair.java @@ -1,50 +1,50 @@ -package com.avaje.ebean; - -/** - * Holds two values as the result of a difference comparison. - */ -public class ValuePair { - - private final Object newValue; - - private final Object oldValue; - - public ValuePair(Object newValue, Object oldValue) { - this.newValue = newValue; - this.oldValue = oldValue; - } - - /** - * Return the new value. - */ - public Object getNewValue() { - return newValue; - } - - /** - * Return the old value. - */ - public Object getOldValue() { - return oldValue; - } - - /** - * Return the new value. - */ - @Deprecated - public Object getValue1() { - return newValue; - } - - /** - * Return the old value. - */ - @Deprecated - public Object getValue2() { - return oldValue; - } - - public String toString() { - return newValue + "," + oldValue; - } -} +package com.avaje.ebean; + +/** + * Holds two values as the result of a difference comparison. + */ +public class ValuePair { + + private final Object newValue; + + private final Object oldValue; + + public ValuePair(Object newValue, Object oldValue) { + this.newValue = newValue; + this.oldValue = oldValue; + } + + /** + * Return the new value. + */ + public Object getNewValue() { + return newValue; + } + + /** + * Return the old value. + */ + public Object getOldValue() { + return oldValue; + } + + /** + * Return the new value. + */ + @Deprecated + public Object getValue1() { + return newValue; + } + + /** + * Return the old value. + */ + @Deprecated + public Object getValue2() { + return oldValue; + } + + public String toString() { + return newValue + "," + oldValue; + } +} 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 61050846b..905087580 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java @@ -1,2020 +1,2020 @@ -package com.avaje.ebeaninternal.server.core; - -import com.avaje.ebean.*; -import com.avaje.ebean.bean.*; -import com.avaje.ebean.bean.PersistenceContext.WithOption; -import com.avaje.ebean.cache.ServerCacheManager; -import com.avaje.ebean.config.EncryptKeyManager; -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebean.event.BeanPersistController; -import com.avaje.ebean.event.BeanQueryAdapter; -import com.avaje.ebean.meta.MetaInfoManager; -import com.avaje.ebean.text.csv.CsvReader; -import com.avaje.ebean.text.json.JsonContext; -import com.avaje.ebeaninternal.api.*; -import com.avaje.ebeaninternal.api.SpiQuery.Mode; -import com.avaje.ebeaninternal.api.SpiQuery.Type; -import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; -import com.avaje.ebeaninternal.server.ddl.DdlGenerator; -import com.avaje.ebeaninternal.server.deploy.*; -import com.avaje.ebeaninternal.server.el.ElFilter; -import com.avaje.ebeaninternal.server.jmx.MAdminAutofetch; -import com.avaje.ebeaninternal.server.lib.ShutdownManager; -import com.avaje.ebeaninternal.server.query.*; -import com.avaje.ebeaninternal.server.querydefn.DefaultOrmQuery; -import com.avaje.ebeaninternal.server.querydefn.DefaultOrmUpdate; -import com.avaje.ebeaninternal.server.querydefn.DefaultRelationalQuery; -import com.avaje.ebeaninternal.server.text.csv.TCsvReader; -import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext; -import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent; -import com.avaje.ebeaninternal.server.transaction.TransactionManager; -import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager; -import com.avaje.ebeaninternal.util.ParamTypeHelper; -import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.management.InstanceAlreadyExistsException; -import javax.management.MBeanServer; -import javax.management.ObjectName; -import javax.persistence.PersistenceException; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.FutureTask; - -/** - * The default server side implementation of EbeanServer. - */ -public final class DefaultServer implements SpiEbeanServer { - - private static final Logger logger = LoggerFactory.getLogger(DefaultServer.class); - - private static final int IGNORE_LEADING_ELEMENTS = 5; - - private static final String AVAJE_EBEAN = Ebean.class.getName().substring(0, 15); - - private final ServerConfig serverConfig; - - private final String serverName; - - private final DatabasePlatform databasePlatform; - - private final AdminAutofetch adminAutofetch; - - private final TransactionManager transactionManager; - - private final TransactionScopeManager transactionScopeManager; - - private final int maxCallStack; - - /** - * Ebean defaults this to true but for EJB compatible behaviour set this to - * false; - */ - private final boolean rollbackOnChecked; - - /** - * Handles the save, delete, updateSql CallableSql. - */ - private final Persister persister; - - private final OrmQueryEngine queryEngine; - - private final RelationalQueryEngine relationalQueryEngine; - - private final ServerCacheManager serverCacheManager; - - private final BeanDescriptorManager beanDescriptorManager; - - private final DiffHelp diffHelp = new DiffHelp(); - - private final AutoFetchManager autoFetchManager; - - private final CQueryEngine cqueryEngine; - - private DdlGenerator ddlGenerator; - - private final ExpressionFactory expressionFactory; - - private final SpiBackgroundExecutor backgroundExecutor; - - private final DefaultBeanLoader beanLoader; - - private final EncryptKeyManager encryptKeyManager; - - private final JsonContext jsonContext; - - private final MetaInfoManager metaInfoManager; - - /** - * The MBean name used to register Ebean. - */ - private String mbeanName; - - /** - * The default PersistenceContextScope used if it is not explicitly set on a query. - */ - private final PersistenceContextScope defaultPersistenceContextScope; - - /** - * The MBeanServer Ebean is registered with. - */ - private MBeanServer mbeanServer; - - /** - * Flag set when the server has shutdown. - */ - private boolean shutdown; - - /** - * The default batch size for lazy loading beans or collections. - */ - private int lazyLoadBatchSize; - - /** - * The query batch size - */ - private int queryBatchSize; - - /** - * JDBC driver specific handling for JDBC batch execution. - */ - private PstmtBatch pstmtBatch; - - /** - * holds plugins (e.g. ddl generator) detected by the service loader - */ - private List ebeanPlugins; - - private final boolean collectQueryOrigins; - - private final boolean collectQueryStatsByNode; - - /** - * Cache used to collect statistics based on ObjectGraphNode (used to highlight lazy loading origin points). - */ - protected final ConcurrentHashMap objectGraphStats; - - /** - * Create the DefaultServer. - */ - public DefaultServer(InternalConfiguration config, ServerCacheManager cache) { - - this.serverConfig = config.getServerConfig(); - this.objectGraphStats = new ConcurrentHashMap(); - this.metaInfoManager = new DefaultMetaInfoManager(this); - this.serverCacheManager = cache; - this.pstmtBatch = config.getPstmtBatch(); - this.databasePlatform = config.getDatabasePlatform(); - this.backgroundExecutor = config.getBackgroundExecutor(); - - this.serverName = serverConfig.getName(); - this.lazyLoadBatchSize = serverConfig.getLazyLoadBatchSize(); - this.queryBatchSize = serverConfig.getQueryBatchSize(); - this.cqueryEngine = config.getCQueryEngine(); - this.expressionFactory = config.getExpressionFactory(); - this.encryptKeyManager = serverConfig.getEncryptKeyManager(); - this.defaultPersistenceContextScope = serverConfig.getPersistenceContextScope(); - - this.beanDescriptorManager = config.getBeanDescriptorManager(); - beanDescriptorManager.setEbeanServer(this); - - this.collectQueryOrigins = serverConfig.isCollectQueryOrigins(); - this.collectQueryStatsByNode = serverConfig.isCollectQueryStatsByNode(); - this.maxCallStack = serverConfig.getMaxCallStack(); - - this.rollbackOnChecked = serverConfig.isTransactionRollbackOnChecked(); - this.transactionManager = config.getTransactionManager(); - this.transactionScopeManager = config.getTransactionScopeManager(); - - this.persister = config.createPersister(this); - this.queryEngine = config.createOrmQueryEngine(); - this.relationalQueryEngine = config.createRelationalQueryEngine(); - - this.autoFetchManager = config.createAutoFetchManager(this); - this.adminAutofetch = new MAdminAutofetch(autoFetchManager); - - this.beanLoader = new DefaultBeanLoader(this); - this.jsonContext = config.createJsonContext(this); - - loadAndInitializePlugins(config); - - // Register with the JVM Shutdown hook - ShutdownManager.registerEbeanServer(this); - } - - protected void loadAndInitializePlugins(InternalConfiguration config) { - - List spiPlugins = new ArrayList(); - - for (SpiEbeanPlugin plugin : ServiceLoader.load(SpiEbeanPlugin.class)) { - spiPlugins.add(plugin); - plugin.setup(this, this.getDatabasePlatform(), config.getServerConfig()); - - if (plugin instanceof DdlGenerator) { - // backwards compatible - ddlGenerator = (DdlGenerator) plugin; - } - } - - if (ddlGenerator == null) { - // ServiceLoader not finding ddlGenerator (typically OSGi) - ddlGenerator = new DdlGenerator(); - spiPlugins.add(ddlGenerator); - ddlGenerator.setup(this, this.getDatabasePlatform(), config.getServerConfig()); - } - - ebeanPlugins = Collections.unmodifiableList(spiPlugins); - } - - public List getSpiEbeanPlugins() { - return ebeanPlugins; - } - - public void executePlugins(boolean online) { - for(SpiEbeanPlugin plugin : ebeanPlugins) { - plugin.execute(online); - } - } - - @Override - public boolean isCollectQueryOrigins() { - return collectQueryOrigins; - } - - public int getLazyLoadBatchSize() { - return lazyLoadBatchSize; - } - - public PstmtBatch getPstmtBatch() { - return pstmtBatch; - } - - public ServerConfig getServerConfig() { - return serverConfig; - } - - public DatabasePlatform getDatabasePlatform() { - return databasePlatform; - } - - @Override - public MetaInfoManager getMetaInfoManager() { - return metaInfoManager; - } - - public BackgroundExecutor getBackgroundExecutor() { - return backgroundExecutor; - } - - public ExpressionFactory getExpressionFactory() { - return expressionFactory; - } - - public DdlGenerator getDdlGenerator() { - return ddlGenerator; - } - - public AdminAutofetch getAdminAutofetch() { - return adminAutofetch; - } - - public AutoFetchManager getAutoFetchManager() { - return autoFetchManager; - } - - /** - * Run any initialisation required before registering with the ClusterManager. - */ - public void initialise() { - if (encryptKeyManager != null) { - encryptKeyManager.initialise(); - } - List> list = beanDescriptorManager.getBeanDescriptorList(); - for (int i = 0; i < list.size(); i++) { - list.get(i).cacheInitialise(); - } - - } - - /** - * Start any services after registering with the ClusterManager. - */ - public void start() { - } - - public void registerMBeans(MBeanServer mbeanServer, int uniqueServerId) { - - this.mbeanServer = mbeanServer; - this.mbeanName = "Ebean:server=" + serverName + uniqueServerId; - - ObjectName autofetchName; - try { - autofetchName = new ObjectName(mbeanName + ",key=AutoFetch"); - } catch (Exception e) { - String msg = "Failed to register the JMX beans for Ebean server [" + serverName + "]."; - logger.error(msg, e); - return; - } - - try { - mbeanServer.registerMBean(adminAutofetch, autofetchName); - - } catch (InstanceAlreadyExistsException e) { - // tomcat webapp reloading - String msg = "JMX beans for Ebean server [" + serverName + "] already registered. Will try unregister/register" + e.getMessage(); - logger.warn(msg); - try { - mbeanServer.unregisterMBean(autofetchName); - mbeanServer.registerMBean(adminAutofetch, autofetchName); - - } catch (Exception ae) { - String amsg = "Unable to unregister/register the JMX beans for Ebean server [" + serverName + "]."; - logger.error(amsg, ae); - } - } catch (Exception e) { - String msg = "Error registering MBean[" + mbeanName + "]"; - logger.error(msg, e); - } - } - - /** - * Shutting down via JVM Shutdown hook. - */ - public void shutdownManaged() { - synchronized (this) { - shutdownInternal(true, false); - } - } - - /** - * Shutting down manually. - */ - public void shutdown(boolean shutdownDataSource, boolean deregisterDriver) { - synchronized (this) { - // Unregister from JVM Shutdown hook - ShutdownManager.unregisterEbeanServer(this); - shutdownInternal(shutdownDataSource, deregisterDriver); - } - } - - /** - * Shutdown the services like threads and DataSource. - */ - private void shutdownInternal(boolean shutdownDataSource, boolean deregisterDriver) { - - logger.debug("Shutting down EbeanServer " + getName()); - if (shutdown) { - // Already shutdown - return; - } - try { - if (mbeanServer != null) { - mbeanServer.unregisterMBean(new ObjectName(mbeanName + ",key=AutoFetch")); - } - } catch (Exception e) { - logger.error("Error unregistering Ebean " + mbeanName, e); - } - - // shutdown autofetch profile collection - autoFetchManager.shutdown(); - // shutdown background threads - backgroundExecutor.shutdown(); - // shutdown DataSource (if its an Ebean one) - transactionManager.shutdown(shutdownDataSource, deregisterDriver); - shutdown = true; - } - - /** - * Return the server name. - */ - public String getName() { - return serverName; - } - - public BeanState getBeanState(Object bean) { - if (bean instanceof EntityBean) { - return new DefaultBeanState((EntityBean) bean); - } - // Not an entity bean - return null; - } - - /** - * Run the cache warming queries on all beans that have them defined. - */ - public void runCacheWarming() { - List> descList = beanDescriptorManager.getBeanDescriptorList(); - for (int i = 0; i < descList.size(); i++) { - descList.get(i).runCacheWarming(); - } - } - - public void runCacheWarming(Class beanType) { - BeanDescriptor desc = beanDescriptorManager.getBeanDescriptor(beanType); - if (desc == null) { - String msg = "Is " + beanType + " an entity? Could not find a BeanDescriptor"; - throw new PersistenceException(msg); - } else { - desc.runCacheWarming(); - } - } - - /** - * Compile a query. Only valid for ORM queries. - */ - public CQuery compileQuery(Query query, Transaction t) { - SpiOrmQueryRequest qr = createQueryRequest(Type.SUBQUERY, query, t); - OrmQueryRequest orm = (OrmQueryRequest) qr; - return cqueryEngine.buildQuery(orm); - } - - public CQueryEngine getQueryEngine() { - return cqueryEngine; - } - - public ServerCacheManager getServerCacheManager() { - return serverCacheManager; - } - - public void refreshMany(Object parentBean, String propertyName, Transaction t) { - - beanLoader.refreshMany(checkEntityBean(parentBean), propertyName, t); - } - - public void refreshMany(Object parentBean, String propertyName) { - - beanLoader.refreshMany(checkEntityBean(parentBean), propertyName); - } - - public void loadMany(LoadManyRequest loadRequest) { - - beanLoader.loadMany(loadRequest); - } - - public void loadMany(BeanCollection bc, boolean onlyIds) { - - beanLoader.loadMany(bc, onlyIds); - } - - public void refresh(Object bean) { - - beanLoader.refresh(checkEntityBean(bean)); - } - - public void loadBean(LoadBeanRequest loadRequest) { - - beanLoader.loadBean(loadRequest); - } - - public void loadBean(EntityBeanIntercept ebi) { - - beanLoader.loadBean(ebi); - } - - public Map diff(Object a, Object b) { - if (a == null) { - return null; - } - - BeanDescriptor desc = getBeanDescriptor(a.getClass()); - return diffHelp.diff(a, b, desc); - } - - /** - * Process committed beans from another framework or server in another - * cluster. - *

    - * This notifies this instance of the framework that beans have been committed - * externally to it. Either by another framework or clustered server. It needs - * to maintain its cache and text indexes appropriately. - *

    - */ - public void externalModification(TransactionEventTable tableEvent) { - SpiTransaction t = transactionScopeManager.get(); - if (t != null) { - t.getEvent().add(tableEvent); - } else { - transactionManager.externalModification(tableEvent); - } - } - - /** - * Developer informing eBean that tables where modified outside of eBean. - * Invalidate the cache etc as required. - */ - public void externalModification(String tableName, boolean inserts, boolean updates, boolean deletes) { - - TransactionEventTable evt = new TransactionEventTable(); - evt.add(tableName, inserts, updates, deletes); - - externalModification(evt); - } - - /** - * Clear the query execution statistics. - */ - public void clearQueryStatistics() { - for (BeanDescriptor desc : getBeanDescriptors()) { - desc.clearQueryStatistics(); - } - } - - /** - * Create a new EntityBean bean. - *

    - * This will generally return a subclass of the parameter 'type' which - * additionally implements the EntityBean interface. That is, the returned - * bean is typically an instance of a dynamically generated class. - *

    - */ - @SuppressWarnings("unchecked") - public T createEntityBean(Class type) { - BeanDescriptor desc = getBeanDescriptor(type); - return (T) desc.createEntityBean(); - } - - /** - * Return a Reference bean. - *

    - * If a current transaction is active then this will check the Context of that - * transaction to see if the bean is already loaded. If it is already loaded - * then it will returned that object. - *

    - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public T getReference(Class type, Object id) { - - if (id == null) { - throw new NullPointerException("The id is null"); - } - - BeanDescriptor desc = getBeanDescriptor(type); - // convert the id type if necessary - id = desc.convertId(id); - - Object ref = null; - PersistenceContext ctx = null; - - SpiTransaction t = transactionScopeManager.get(); - if (t != null) { - // first try the persistence context - ctx = t.getPersistenceContext(); - ref = ctx.get(type, id); - } - - if (ref == null) { - InheritInfo inheritInfo = desc.getInheritInfo(); - if (inheritInfo != null) { - // we actually need to do a query because - // we don't know the type without the - // discriminator value - BeanProperty idProp = desc.getIdProperty(); - if (idProp == null) { - throw new PersistenceException("No ID properties for this type? " + desc); - } - - // just select the id properties and - // the discriminator column (auto added) - Query query = createQuery(type); - query.select(idProp.getName()).setId(id); - - ref = query.findUnique(); - - } else { - // use the default reference options - ref = desc.createReference(null, id); - } - - if (ctx != null && (ref instanceof EntityBean)) { - // Not putting a vanilla reference in the persistence context - ctx.put(id, ref); - } - } - return (T) ref; - } - - @Override - public void register(TransactionCallback transactionCallback) { - Transaction transaction = currentTransaction(); - if (transaction == null) { - throw new PersistenceException("Not currently active transaction when trying to register transactionCallback"); - } - transaction.register(transactionCallback); - } - - /** - * Creates a new Transaction that is NOT stored in TransactionThreadLocal. Use - * this when you want a thread to have a second independent transaction. - */ - public Transaction createTransaction() { - - return transactionManager.createTransaction(true, -1); - } - - /** - * Create a transaction additionally specify the Isolation level. - *

    - * Note that this transaction is not stored in a thread local. - *

    - */ - public Transaction createTransaction(TxIsolation isolation) { - - return transactionManager.createTransaction(true, isolation.getLevel()); - } - - public T execute(TxCallable c) { - return execute(null, c); - } - - public T execute(TxScope scope, TxCallable c) { - ScopeTrans scopeTrans = createScopeTrans(scope); - try { - return c.call(); - - } catch (Error e) { - throw scopeTrans.caughtError(e); - - } catch (RuntimeException e) { - throw scopeTrans.caughtThrowable(e); - - } finally { - scopeTrans.onFinally(); - } - } - - public void execute(TxRunnable r) { - execute(null, r); - } - - public void execute(TxScope scope, TxRunnable r) { - ScopeTrans scopeTrans = createScopeTrans(scope); - try { - r.run(); - - } catch (Error e) { - throw scopeTrans.caughtError(e); - - } catch (RuntimeException e) { - throw scopeTrans.caughtThrowable(e); - - } finally { - scopeTrans.onFinally(); - } - } - - /** - * Determine whether to create a new transaction or not. - *

    - * This will also potentially throw exceptions for MANDATORY and NEVER types. - *

    - */ - private boolean createNewTransaction(SpiTransaction t, TxScope scope) { - - TxType type = scope.getType(); - switch (type) { - case REQUIRED: - return t == null; - - case REQUIRES_NEW: - return true; - - case MANDATORY: - if (t == null) { - throw new PersistenceException("Transaction missing when MANDATORY"); - } - return true; - - case NEVER: - if (t != null) { - throw new PersistenceException("Transaction exists for Transactional NEVER"); - } - return false; - - case SUPPORTS: - return false; - - case NOT_SUPPORTED: - throw new RuntimeException("NOT_SUPPORTED should already be handled?"); - - default: - throw new RuntimeException("Should never get here?"); - } - } - - public ScopeTrans createScopeTrans(TxScope txScope) { - - if (txScope == null) { - // create a TxScope with default settings - txScope = new TxScope(); - } - - SpiTransaction suspended = null; - - // get current transaction from ThreadLocal or equivalent - SpiTransaction t = transactionScopeManager.get(); - - boolean newTransaction; - if (txScope.getType().equals(TxType.NOT_SUPPORTED)) { - // Suspend existing transaction and - // run without a transaction in scope - newTransaction = false; - suspended = t; - t = null; - - } else { - // create a new Transaction based on TxType and t - newTransaction = createNewTransaction(t, txScope); - - if (newTransaction) { - // suspend existing transaction (if there is one) - suspended = t; - - // create a new transaction - int isoLevel = -1; - TxIsolation isolation = txScope.getIsolation(); - if (isolation != null) { - isoLevel = isolation.getLevel(); - } - t = transactionManager.createTransaction(true, isoLevel); - } - } - - // replace the current transaction ... ScopeTrans.onFinally() - // has the job of restoring the suspended transaction - transactionScopeManager.replace(t); - - return new ScopeTrans(rollbackOnChecked, newTransaction, t, txScope, suspended, transactionScopeManager); - } - - /** - * Returns the current transaction (or null) from the scope. - */ - public SpiTransaction getCurrentServerTransaction() { - return transactionScopeManager.get(); - } - - /** - * Start a transaction. - *

    - * Note that the transaction is stored in a ThreadLocal variable. - *

    - */ - public Transaction beginTransaction() { - // start an explicit transaction - SpiTransaction t = transactionManager.createTransaction(true, -1); - transactionScopeManager.set(t); - return t; - } - - public Transaction beginTransaction(TxScope scope) { - ScopeTrans scopeTrans = createScopeTrans(scope); - return new ScopedTransaction(scopeTrans); - } - - /** - * Start a transaction with a specific Isolation Level. - *

    - * Note that the transaction is stored in a ThreadLocal variable. - *

    - */ - public Transaction beginTransaction(TxIsolation isolation) { - // start an explicit transaction - SpiTransaction t = transactionManager.createTransaction(true, isolation.getLevel()); - transactionScopeManager.set(t); - return t; - } - - /** - * Return the current transaction or null if there is not one currently in - * scope. - */ - public Transaction currentTransaction() { - return transactionScopeManager.get(); - } - - /** - * Commit the current transaction. - */ - public void commitTransaction() { - transactionScopeManager.commit(); - } - - /** - * Rollback the current transaction. - */ - public void rollbackTransaction() { - transactionScopeManager.rollback(); - } - - /** - * If the current transaction has already been committed do nothing otherwise - * rollback the transaction. - *

    - * Useful to put in a finally block to ensure the transaction is ended, rather - * than a rollbackTransaction() in each catch block. - *

    - *

    - * Code example:
    - * - *

    -   * <code>
    -   * Ebean.startTransaction();
    -   * try {
    -   * 	// do some fetching and or persisting
    -   * 
    -   * 	// commit at the end
    -   * 	Ebean.commitTransaction();
    -   * 
    -   * } finally {
    -   * 	// if commit didn't occur then rollback the transaction
    -   * 	Ebean.endTransaction();
    -   * }
    -   * </code>
    -   * 
    - * - *

    - */ - public void endTransaction() { - transactionScopeManager.end(); - } - - /** - * return the next unique identity value. - *

    - * Uses the BeanDescriptor deployment information to determine the sequence to - * use. - *

    - */ - public Object nextId(Class beanType) { - BeanDescriptor desc = getBeanDescriptor(beanType); - return desc.nextId(null); - } - - @SuppressWarnings("unchecked") - public void sort(List list, String sortByClause) { - - if (list == null) { - throw new NullPointerException("list is null"); - } - if (sortByClause == null) { - throw new NullPointerException("sortByClause is null"); - } - if (list.size() == 0) { - // don't need to sort an empty list - return; - } - // use first bean in the list as the correct type - Class beanType = (Class) list.get(0).getClass(); - BeanDescriptor beanDescriptor = getBeanDescriptor(beanType); - if (beanDescriptor == null) { - String m = "BeanDescriptor not found, is [" + beanType + "] an entity bean?"; - throw new PersistenceException(m); - } - beanDescriptor.sort(list, sortByClause); - } - - public Query createQuery(Class beanType) throws PersistenceException { - return createQuery(beanType, null); - } - - public Query createNamedQuery(Class beanType, String namedQuery) throws PersistenceException { - - BeanDescriptor desc = getBeanDescriptor(beanType); - if (desc == null) { - throw new PersistenceException("Is " + beanType.getName() + " an Entity Bean? BeanDescriptor not found?"); - } - DeployNamedQuery deployQuery = desc.getNamedQuery(namedQuery); - if (deployQuery == null) { - throw new PersistenceException("named query " + namedQuery + " was not found for " + desc.getFullName()); - } - - // this will parse the query - return new DefaultOrmQuery(beanType, this, expressionFactory, deployQuery); - } - - public Filter filter(Class beanType) { - BeanDescriptor desc = getBeanDescriptor(beanType); - if (desc == null) { - String m = beanType.getName() + " is NOT an Entity Bean registered with this server?"; - throw new PersistenceException(m); - } - return new ElFilter(desc); - } - - public CsvReader createCsvReader(Class beanType) { - BeanDescriptor descriptor = getBeanDescriptor(beanType); - if (descriptor == null) { - throw new NullPointerException("BeanDescriptor for " + beanType.getName() + " not found"); - } - return new TCsvReader(this, descriptor); - } - - public Query find(Class beanType) { - return createQuery(beanType); - } - - public Query createQuery(Class beanType, String query) { - BeanDescriptor desc = getBeanDescriptor(beanType); - if (desc == null) { - String m = beanType.getName() + " is NOT an Entity Bean registered with this server?"; - throw new PersistenceException(m); - } - switch (desc.getEntityType()) { - case SQL: - if (query != null) { - throw new PersistenceException("You must used Named queries for this Entity " + desc.getFullName()); - } - // use the "default" SqlSelect - DeployNamedQuery defaultSqlSelect = desc.getNamedQuery("default"); - return new DefaultOrmQuery(beanType, this, expressionFactory, defaultSqlSelect); - - default: - return new DefaultOrmQuery(beanType, this, expressionFactory, query); - } - } - - public Update createNamedUpdate(Class beanType, String namedUpdate) { - BeanDescriptor desc = getBeanDescriptor(beanType); - if (desc == null) { - String m = beanType.getName() + " is NOT an Entity Bean registered with this server?"; - throw new PersistenceException(m); - } - - DeployNamedUpdate deployUpdate = desc.getNamedUpdate(namedUpdate); - if (deployUpdate == null) { - throw new PersistenceException("named update " + namedUpdate + " was not found for " + desc.getFullName()); - } - - return new DefaultOrmUpdate(beanType, this, desc.getBaseTable(), deployUpdate); - } - - public Update createUpdate(Class beanType, String ormUpdate) { - BeanDescriptor desc = getBeanDescriptor(beanType); - if (desc == null) { - String m = beanType.getName() + " is NOT an Entity Bean registered with this server?"; - throw new PersistenceException(m); - } - - return new DefaultOrmUpdate(beanType, this, desc.getBaseTable(), ormUpdate); - } - - public SqlQuery createSqlQuery(String sql) { - return new DefaultRelationalQuery(this, sql); - } - - public SqlQuery createNamedSqlQuery(String namedQuery) { - DNativeQuery nq = beanDescriptorManager.getNativeQuery(namedQuery); - if (nq == null) { - throw new PersistenceException("SqlQuery " + namedQuery + " not found."); - } - return new DefaultRelationalQuery(this, nq.getQuery()); - } - - public SqlUpdate createSqlUpdate(String sql) { - return new DefaultSqlUpdate(this, sql); - } - - public CallableSql createCallableSql(String sql) { - return new DefaultCallableSql(this, sql); - } - - public SqlUpdate createNamedSqlUpdate(String namedQuery) { - DNativeQuery nq = beanDescriptorManager.getNativeQuery(namedQuery); - if (nq == null) { - throw new PersistenceException("SqlUpdate " + namedQuery + " not found."); - } - return new DefaultSqlUpdate(this, nq.getQuery()); - } - - public T find(Class beanType, Object uid) { - - return find(beanType, uid, null); - } - - /** - * Find a bean using its unique id. - */ - public T find(Class beanType, Object id, Transaction t) { - - if (id == null) { - throw new NullPointerException("The id is null"); - } - - Query query = createQuery(beanType).setId(id); - return findId(query, t); - } - - private SpiOrmQueryRequest createQueryRequest(Type type, Query query, Transaction t) { - - SpiQuery spiQuery = (SpiQuery) query; - spiQuery.setType(type); - - BeanDescriptor desc = beanDescriptorManager.getBeanDescriptor(spiQuery.getBeanType()); - spiQuery.setBeanDescriptor(desc); - - return createQueryRequest(desc, spiQuery, t); - } - - public SpiOrmQueryRequest createQueryRequest(BeanDescriptor desc, SpiQuery query, Transaction t) { - - if (desc.isAutoFetchTunable() && !query.isSqlSelect() && !autoFetchManager.tuneQuery(query)) { - // use deployment FetchType.LAZY/EAGER annotations - // to define the 'default' select clause - query.setDefaultSelectClause(); - } - - if (query.selectAllForLazyLoadProperty()) { - // we need to select all properties to ensure the lazy load property - // was included (was not included by default or via autofetch). - if (logger.isDebugEnabled()) { - logger.debug("Using selectAllForLazyLoadProperty"); - } - } - - // if determine cost and no origin for Autofetch - if (query.getParentNode() == null) { - query.setOrigin(createCallStack()); - } - - // determine extra joins required to support where clause - // predicates on *ToMany properties - if (query.initManyWhereJoins()) { - // we need a sql distinct now - query.setSqlDistinct(true); - } - - boolean allowOneManyFetch = true; - if (Mode.LAZYLOAD_MANY.equals(query.getMode())) { - allowOneManyFetch = false; - - } else if (query.hasMaxRowsOrFirstRow() && !query.isRawSql() && !query.isSqlSelect()) { - // convert ALL fetch joins to Many's to be query joins - // so that limit offset type SQL clauses work - allowOneManyFetch = false; - } - - query.convertManyFetchJoinsToQueryJoins(allowOneManyFetch, queryBatchSize); - - SpiTransaction serverTrans = (SpiTransaction) t; - OrmQueryRequest request = new OrmQueryRequest(this, queryEngine, query, desc, serverTrans); - - BeanQueryAdapter queryAdapter = desc.getQueryAdapter(); - if (queryAdapter != null) { - // adaption of the query probably based on the - // current user - queryAdapter.preQuery(request); - } - - // the query hash after any tuning - request.calculateQueryPlanHash(); - - return request; - } - - /** - * Try to get the object out of the persistence context. - */ - @SuppressWarnings("unchecked") - private T findIdCheckPersistenceContextAndCache(Transaction transaction, BeanDescriptor beanDescriptor, SpiQuery query) { - - SpiTransaction t = (SpiTransaction) transaction; - if (t == null) { - t = getCurrentServerTransaction(); - } - PersistenceContext context = null; - if (t != null && useTransactionPersistenceContext(query)) { - // first look in the transaction scoped persistence context - context = t.getPersistenceContext(); - if (context != null) { - WithOption o = context.getWithOption(beanDescriptor.getBeanType(), query.getId()); - if (o != null) { - if (o.isDeleted()) { - // Bean was previously deleted in the same transaction / persistence context - return null; - } - // Return the entity bean instance from the persistence context - return (T) o.getBean(); - } - } - } - - if (!beanDescriptor.calculateUseCache(query.isUseBeanCache())) { - // not using bean cache - return null; - } - - // Hit the L2 bean cache - return beanDescriptor.cacheBeanGet(query, context); - } - - /** - * Return true if transactions PersistenceContext should be used. - */ - private boolean useTransactionPersistenceContext(SpiQuery query) { - return PersistenceContextScope.TRANSACTION.equals(getPersistenceContextScope(query)); - } - - /** - * Return the PersistenceContextScope to use defined at query or server level. - */ - public PersistenceContextScope getPersistenceContextScope(SpiQuery query) { - PersistenceContextScope scope = query.getPersistenceContextScope(); - return (scope != null) ? scope : defaultPersistenceContextScope; - } - - @SuppressWarnings("unchecked") - private T findId(Query query, Transaction t) { - - SpiQuery spiQuery = (SpiQuery) query; - spiQuery.setType(Type.BEAN); - - BeanDescriptor desc = beanDescriptorManager.getBeanDescriptor(spiQuery.getBeanType()); - spiQuery.setBeanDescriptor(desc); - - if (SpiQuery.Mode.NORMAL.equals(spiQuery.getMode()) && !spiQuery.isLoadBeanCache()) { - // See if we can skip doing the fetch completely by getting the bean from the - // persistence context or the bean cache - T bean = findIdCheckPersistenceContextAndCache(t, desc, spiQuery); - if (bean != null) { - return bean; - } - } - - SpiOrmQueryRequest request = createQueryRequest(desc, spiQuery, t); - try { - request.initTransIfRequired(); - return (T) request.findId(); - - } finally { - request.endTransIfRequired(); - } - } - - public T findUnique(Query query, Transaction t) { - - // actually a find by Id type of query... - // ... perhaps with joins and cache hints? - SpiQuery q = (SpiQuery) query; - Object id = q.getId(); - if (id != null) { - return findId(query, t); - } - - BeanDescriptor desc = beanDescriptorManager.getBeanDescriptor(q.getBeanType()); - - T bean = desc.cacheNaturalKeyLookup(q, (SpiTransaction)t); - if (bean != null) { - return bean; - } - - // a query that is expected to return either 0 or 1 rows - List list = findList(query, t); - - if (list.size() == 0) { - return null; - - } else if (list.size() > 1) { - throw new PersistenceException("Unique expecting 0 or 1 rows but got [" + list.size() + "]"); - - } else { - return list.get(0); - } - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public Set findSet(Query query, Transaction t) { - - SpiOrmQueryRequest request = createQueryRequest(Type.SET, query, t); - - Object result = request.getFromQueryCache(); - if (result != null) { - return (Set) result; - } - - try { - request.initTransIfRequired(); - return (Set) request.findSet(); - - } finally { - request.endTransIfRequired(); - } - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public Map findMap(Query query, Transaction t) { - - SpiOrmQueryRequest request = createQueryRequest(Type.MAP, query, t); - - Object result = request.getFromQueryCache(); - if (result != null) { - return (Map) result; - } - - try { - request.initTransIfRequired(); - return (Map) request.findMap(); - - } finally { - request.endTransIfRequired(); - } - } - - public int findRowCount(Query query, Transaction t) { - - SpiQuery copy = ((SpiQuery) query).copy(); - return findRowCountWithCopy(copy, t); - } - - public int findRowCountWithCopy(Query query, Transaction t) { - - SpiOrmQueryRequest request = createQueryRequest(Type.ROWCOUNT, query, t); - try { - request.initTransIfRequired(); - return request.findRowCount(); - - } finally { - request.endTransIfRequired(); - } - } - - public List findIds(Query query, Transaction t) { - - SpiQuery copy = ((SpiQuery) query).copy(); - - return findIdsWithCopy(copy, t); - } - - public List findIdsWithCopy(Query query, Transaction t) { - - SpiOrmQueryRequest request = createQueryRequest(Type.ID_LIST, query, t); - try { - request.initTransIfRequired(); - return request.findIds(); - - } finally { - request.endTransIfRequired(); - } - } - - public FutureRowCount findFutureRowCount(Query q, Transaction t) { - - SpiQuery copy = ((SpiQuery) q).copy(); - copy.setFutureFetch(true); - - Transaction newTxn = createTransaction(); - - CallableQueryRowCount call = new CallableQueryRowCount(this, copy, newTxn); - - QueryFutureRowCount queryFuture = new QueryFutureRowCount(call); - backgroundExecutor.execute(queryFuture.getFutureTask()); - - return queryFuture; - } - - 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); - QueryFutureIds queryFuture = new QueryFutureIds(call); - - backgroundExecutor.execute(queryFuture.getFutureTask()); - - return queryFuture; - } - - public FutureList findFutureList(Query query, Transaction t) { - - SpiQuery spiQuery = (SpiQuery) query; - spiQuery.setFutureFetch(true); - - // FutureList query always run in it's own persistence content - spiQuery.setPersistenceContext(new DefaultPersistenceContext()); - - // Create a new transaction solely to execute the findList() at some future time - Transaction newTxn = createTransaction(); - CallableQueryList call = new CallableQueryList(this, spiQuery, newTxn); - QueryFutureList queryFuture = new QueryFutureList(call); - backgroundExecutor.execute(queryFuture.getFutureTask()); - return queryFuture; - } - - @Override - public PagedList findPagedList(Query query, Transaction transaction, int pageIndex, int pageSize) { - - return new LimitOffsetPagedList(this, (SpiQuery)query, pageIndex, pageSize); - } - - public void findVisit(Query query, QueryResultVisitor visitor, Transaction t) { - - SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, t); - - request.initTransIfRequired(); - request.findVisit(visitor); - // no try finally - findVisit guarantee's cleanup of the transaction if required - } - - public void findEach(Query query, QueryEachConsumer consumer, Transaction t) { - - SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, t); - - request.initTransIfRequired(); - request.findEach(consumer); - // no try finally - findVisit guarantee's cleanup of the transaction if required - } - - public void findEachWhile(Query query, QueryEachWhileConsumer consumer, Transaction t) { - - SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, t); - - request.initTransIfRequired(); - request.findEachWhile(consumer); - // no try finally - findVisit guarantee's cleanup of the transaction if required - } - - public QueryIterator findIterate(Query query, Transaction t) { - - SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, t); - - try { - request.initTransIfRequired(); - return request.findIterate(); - - } catch (RuntimeException ex) { - request.endTransIfRequired(); - throw ex; - } - } - - @SuppressWarnings("unchecked") - public List findList(Query query, Transaction t) { - - SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, t); - - Object result = request.getFromQueryCache(); - if (result != null) { - return (List) result; - } - - try { - request.initTransIfRequired(); - return request.findList(); - - } finally { - request.endTransIfRequired(); - } - } - - public SqlRow findUnique(SqlQuery query, Transaction t) { - - // no findId() method for SqlQuery... - // a query that is expected to return either 0 or 1 rows - List list = findList(query, t); - - if (list.size() == 0) { - return null; - - } else if (list.size() > 1) { - String m = "Unique expecting 0 or 1 rows but got [" + list.size() + "]"; - throw new PersistenceException(m); - - } else { - return list.get(0); - } - } - - public SqlFutureList findFutureList(SqlQuery query, Transaction t) { - - SpiSqlQuery spiQuery = (SpiSqlQuery) query; - spiQuery.setFutureFetch(true); - - Transaction newTxn = createTransaction(); - CallableSqlQueryList call = new CallableSqlQueryList(this, query, newTxn); - - FutureTask> futureTask = new FutureTask>(call); - - backgroundExecutor.execute(futureTask); - - return new SqlQueryFutureList(query, futureTask); - } - - public List findList(SqlQuery query, Transaction t) { - - RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t); - - try { - request.initTransIfRequired(); - return request.findList(); - - } finally { - request.endTransIfRequired(); - } - } - - public Set findSet(SqlQuery query, Transaction t) { - - RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t); - - try { - request.initTransIfRequired(); - return request.findSet(); - - } finally { - request.endTransIfRequired(); - } - } - - public Map findMap(SqlQuery query, Transaction t) { - - RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t); - try { - request.initTransIfRequired(); - return request.findMap(); - - } finally { - request.endTransIfRequired(); - } - } - - /** - * Persist the bean by either performing an insert or update. - */ - public void save(Object bean) { - save(bean, null); - } - - /** - * Save the bean with an explicit transaction. - */ - public void save(Object bean, Transaction t) { - persister.save(checkEntityBean(bean), t); - } - - - @Override - public void markAsDirty(Object bean) { - if (!(bean instanceof EntityBean)) { - throw new IllegalArgumentException("This bean is not an EntityBean?"); - } - // mark the bean as dirty (so that an update will not get skipped) - ((EntityBean)bean)._ebean_getIntercept().setDirty(true); - } - - /** - * Update the bean using the default 'updatesDeleteMissingChildren' setting. - */ - public void update(Object bean) { - update(bean, null); - } - - /** - * Update the bean using the default 'updatesDeleteMissingChildren' setting. - */ - public void update(Object bean, Transaction t) { - persister.update(checkEntityBean(bean), t); - } - - /** - * Update the bean specifying the deleteMissingChildren option. - */ - public void update(Object bean, Transaction t, boolean deleteMissingChildren) { - persister.update(checkEntityBean(bean), t, deleteMissingChildren); - } - - /** - * Update all beans in the collection. - */ - public void update(Collection beans) { - update(beans, null); - } - - /** - * Update all beans in the collection with an explicit transaction. - */ - public void update(Collection beans, Transaction t) { - - if (beans == null || beans.isEmpty()) { - // Nothing to update? - return; - } - - TransWrapper wrap = initTransIfRequired(t); - try { - SpiTransaction trans = wrap.transaction; - for (Object bean : beans) { - update(checkEntityBean(bean), trans); - } - wrap.commitIfCreated(); - - } catch (RuntimeException e) { - wrap.rollbackIfCreated(); - throw e; - } - } - - /** - * Insert the bean. - */ - public void insert(Object bean) { - insert(bean, null); - } - - /** - * Insert the bean with a transaction. - */ - public void insert(Object bean, Transaction t) { - persister.insert(checkEntityBean(bean), t); - } - - /** - * Insert all beans in the collection. - */ - public void insert(Collection beans) { - insert(beans, null); - } - - /** - * Insert all beans in the collection with a transaction. - */ - public void insert(Collection beans, Transaction t) { - - if (beans == null || beans.isEmpty()) { - // Nothing to insert? - return; - } - - TransWrapper wrap = initTransIfRequired(t); - try { - SpiTransaction trans = wrap.transaction; - for (Object bean : beans) { - persister.insert(checkEntityBean(bean), trans); - } - wrap.commitIfCreated(); - - } catch (RuntimeException e) { - wrap.rollbackIfCreated(); - throw e; - } - } - - private EntityBean checkEntityBean(Object bean) { - if (bean == null) { - throw new IllegalArgumentException(Message.msg("bean.isnull")); - } - if (!(bean instanceof EntityBean)) { - throw new IllegalArgumentException("Was expecting an EntityBean but got a "+bean.getClass()); - } - return (EntityBean)bean; - } - - /** - * Delete the associations (from the intersection table) of a ManyToMany given - * the owner bean and the propertyName of the ManyToMany collection. - *

    - * This returns the number of associations deleted. - *

    - */ - public int deleteManyToManyAssociations(Object ownerBean, String propertyName) { - return deleteManyToManyAssociations(ownerBean, propertyName, null); - } - - /** - * Delete the associations (from the intersection table) of a ManyToMany given - * the owner bean and the propertyName of the ManyToMany collection. - *

    - * This returns the number of associations deleted. - *

    - */ - public int deleteManyToManyAssociations(Object ownerBean, String propertyName, Transaction t) { - - EntityBean owner = checkEntityBean(ownerBean); - TransWrapper wrap = initTransIfRequired(t); - try { - SpiTransaction trans = wrap.transaction; - int rc = persister.deleteManyToManyAssociations(owner, propertyName, trans); - wrap.commitIfCreated(); - return rc; - - } catch (RuntimeException e) { - wrap.rollbackIfCreated(); - throw e; - } - } - - /** - * Save the associations of a ManyToMany given the owner bean and the - * propertyName of the ManyToMany collection. - */ - public void saveManyToManyAssociations(Object ownerBean, String propertyName) { - saveManyToManyAssociations(ownerBean, propertyName, null); - } - - /** - * Save the associations of a ManyToMany given the owner bean and the - * propertyName of the ManyToMany collection. - */ - public void saveManyToManyAssociations(Object ownerBean, String propertyName, Transaction t) { - - EntityBean owner = checkEntityBean(ownerBean); - TransWrapper wrap = initTransIfRequired(t); - try { - SpiTransaction trans = wrap.transaction; - - persister.saveManyToManyAssociations(owner, propertyName, trans); - - wrap.commitIfCreated(); - - } catch (RuntimeException e) { - wrap.rollbackIfCreated(); - throw e; - } - } - - public void saveAssociation(Object ownerBean, String propertyName) { - saveAssociation(ownerBean, propertyName, null); - } - - public void saveAssociation(Object ownerBean, String propertyName, Transaction t) { - - EntityBean owner = checkEntityBean(ownerBean); - - TransWrapper wrap = initTransIfRequired(t); - try { - SpiTransaction trans = wrap.transaction; - persister.saveAssociation(owner, propertyName, trans); - - wrap.commitIfCreated(); - - } catch (RuntimeException e) { - wrap.rollbackIfCreated(); - throw e; - } - } - - /** - * Perform an update or insert on each bean in the iterator. Returns the - * number of beans that where saved. - */ - public int save(Iterator it) { - return save(it, null); - } - - /** - * Perform an update or insert on each bean in the collection. Returns the - * number of beans that where saved. - */ - public int save(Collection c) { - return save(c.iterator(), null); - } - - /** - * Perform an update or insert on each bean in the collection. Returns the - * number of beans that where saved. - */ - public int save(Collection c, Transaction t) { - return save(c.iterator(), t); - } - - /** - * Save all beans in the iterator with an explicit transaction. - */ - public int save(Iterator it, Transaction t) { - - TransWrapper wrap = initTransIfRequired(t); - try { - wrap.batchEscalateOnCollection(); - SpiTransaction trans = wrap.transaction; - int saveCount = 0; - while (it.hasNext()) { - EntityBean bean = checkEntityBean(it.next()); - persister.save(bean, trans); - saveCount++; - } - - wrap.commitIfCreated(); - wrap.flushBatchOnCollection(); - return saveCount; - - } catch (RuntimeException e) { - wrap.rollbackIfCreated(); - throw e; - } - } - - public int delete(Class beanType, Object id) { - return delete(beanType, id, null); - } - - public int delete(Class beanType, Object id, Transaction t) { - - TransWrapper wrap = initTransIfRequired(t); - try { - SpiTransaction trans = wrap.transaction; - int rowCount = persister.delete(beanType, id, trans); - wrap.commitIfCreated(); - - return rowCount; - - } catch (RuntimeException e) { - wrap.rollbackIfCreated(); - throw e; - } - } - - public void delete(Class beanType, Collection ids) { - delete(beanType, ids, null); - } - - public void delete(Class beanType, Collection ids, Transaction t) { - - TransWrapper wrap = initTransIfRequired(t); - try { - SpiTransaction trans = wrap.transaction; - persister.deleteMany(beanType, ids, trans); - wrap.commitIfCreated(); - - } catch (RuntimeException e) { - wrap.rollbackIfCreated(); - throw e; - } - } - - /** - * Delete the bean. - */ - public void delete(Object bean) { - delete(bean, null); - } - - /** - * Delete the bean with the explicit transaction. - */ - public void delete(Object bean, Transaction t) { - - persister.delete(checkEntityBean(bean), t); - } - - /** - * Delete all the beans in the iterator. - */ - public int delete(Iterator it) { - return delete(it, null); - } - - /** - * Delete all the beans in the collection. - */ - public int delete(Collection c) { - return delete(c.iterator(), null); - } - - /** - * Delete all the beans in the iterator with an explicit transaction. - */ - public int delete(Iterator it, Transaction t) { - - TransWrapper wrap = initTransIfRequired(t); - - try { - wrap.batchEscalateOnCollection(); - SpiTransaction trans = wrap.transaction; - int deleteCount = 0; - while (it.hasNext()) { - EntityBean bean = checkEntityBean(it.next()); - persister.delete(bean, trans); - deleteCount++; - } - - wrap.commitIfCreated(); - wrap.flushBatchOnCollection(); - return deleteCount; - - } catch (RuntimeException e) { - wrap.rollbackIfCreated(); - throw e; - } - } - - /** - * Execute the CallableSql with an explicit transaction. - */ - public int execute(CallableSql callSql, Transaction t) { - return persister.executeCallable(callSql, t); - } - - /** - * Execute the CallableSql. - */ - public int execute(CallableSql callSql) { - return execute(callSql, null); - } - - /** - * Execute the updateSql with an explicit transaction. - */ - public int execute(SqlUpdate updSql, Transaction t) { - return persister.executeSqlUpdate(updSql, t); - } - - /** - * Execute the updateSql. - */ - public int execute(SqlUpdate updSql) { - return execute(updSql, null); - } - - /** - * Execute the updateSql with an explicit transaction. - */ - public int execute(Update update, Transaction t) { - return persister.executeOrmUpdate(update, t); - } - - /** - * Execute the orm update. - */ - public int execute(Update update) { - return execute(update, null); - } - - /** - * Return all the BeanDescriptors. - */ - public List> getBeanDescriptors() { - return beanDescriptorManager.getBeanDescriptorList(); - } - - public void register(BeanPersistController c) { - List> list = beanDescriptorManager.getBeanDescriptorList(); - for (int i = 0; i < list.size(); i++) { - list.get(i).register(c); - } - } - - public void deregister(BeanPersistController c) { - List> list = beanDescriptorManager.getBeanDescriptorList(); - for (int i = 0; i < list.size(); i++) { - list.get(i).deregister(c); - } - } - - public boolean isSupportedType(java.lang.reflect.Type genericType) { - - TypeInfo typeInfo = ParamTypeHelper.getTypeInfo(genericType); - return typeInfo != null && getBeanDescriptor(typeInfo.getBeanType()) != null; - } - - public Object getBeanId(Object bean) { - EntityBean eb = checkEntityBean(bean); - BeanDescriptor desc = getBeanDescriptor(bean.getClass()); - if (desc == null) { - String m = bean.getClass().getName() + " is NOT an Entity Bean registered with this server?"; - throw new PersistenceException(m); - } - - return desc.getId(eb); - } - - /** - * Return the BeanDescriptor for a given type of bean. - */ - public BeanDescriptor getBeanDescriptor(Class beanClass) { - return beanDescriptorManager.getBeanDescriptor(beanClass); - } - - /** - * Return the BeanDescriptor's for a given table name. - */ - public List> getBeanDescriptors(String tableName) { - return beanDescriptorManager.getBeanDescriptors(tableName); - } - - /** - * Return the BeanDescriptor using its unique id. - */ - public BeanDescriptor getBeanDescriptorById(String descriptorId) { - return beanDescriptorManager.getBeanDescriptorById(descriptorId); - } - - /** - * Another server in the cluster sent this event so that we can inform local - * BeanListeners of inserts updates and deletes that occurred remotely (on - * another server in the cluster). - */ - public void remoteTransactionEvent(RemoteTransactionEvent event) { - transactionManager.remoteTransactionEvent(event); - } - - /** - * Create a transaction if one is not currently active in the - * TransactionThreadLocal. - *

    - * Returns a TransWrapper which contains the wasCreated flag. If this is true - * then the transaction was created for this request in which case it will - * need to be committed after the request has been processed. - *

    - */ - TransWrapper initTransIfRequired(Transaction t) { - - if (t != null) { - return new TransWrapper((SpiTransaction) t, false); - } - - boolean wasCreated = false; - SpiTransaction trans = transactionScopeManager.get(); - if (trans == null) { - // create a transaction - trans = transactionManager.createTransaction(false, -1); - wasCreated = true; - } - return new TransWrapper(trans, wasCreated); - } - - public SpiTransaction createServerTransaction(boolean isExplicit, int isolationLevel) { - return transactionManager.createTransaction(isExplicit, isolationLevel); - } - - public SpiTransaction createQueryTransaction() { - return transactionManager.createQueryTransaction(); - } - - - /** - * Create a CallStack object. - *

    - * This trims off the avaje ebean part of the stack trace so that the first - * element in the CallStack should be application code. - *

    - */ - public CallStack createCallStack() { - - StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); - - // ignore the first 6 as they are always avaje stack elements - int startIndex = IGNORE_LEADING_ELEMENTS; - - // find the first non-avaje stackElement - for (; startIndex < stackTrace.length; startIndex++) { - if (!stackTrace[startIndex].getClassName().startsWith(AVAJE_EBEAN)) { - break; - } - } - - int stackLength = stackTrace.length - startIndex; - if (stackLength > maxCallStack) { - // maximum of maxCallStack stackTrace elements - stackLength = maxCallStack; - } - - // create the 'interesting' part of the stackTrace - StackTraceElement[] finalTrace = new StackTraceElement[stackLength]; - System.arraycopy(stackTrace, startIndex, finalTrace, 0, stackLength); - - if (stackLength < 1) { - // this should not really happen - throw new RuntimeException("StackTraceElement size 0? stack: " + Arrays.toString(stackTrace)); - } - - return new CallStack(finalTrace); - } - - - @Override - public JsonContext json() { - // immutable thread safe so return shared instance - return jsonContext; - } - - @Override - public JsonContext createJsonContext() { - return json(); - } - - - @Override - public void collectQueryStats(ObjectGraphNode node, long loadedBeanCount, long timeMicros) { - - if (collectQueryStatsByNode) { - CObjectGraphNodeStatistics nodeStatistics = objectGraphStats.get(node); - if (nodeStatistics == null) { - // race condition here but I actually don't care too much if we miss a - // few early statistics - especially when the server is warming up etc - nodeStatistics = new CObjectGraphNodeStatistics(node); - objectGraphStats.put(node, nodeStatistics); - } - nodeStatistics.add(loadedBeanCount, timeMicros); - } - } - -} +package com.avaje.ebeaninternal.server.core; + +import com.avaje.ebean.*; +import com.avaje.ebean.bean.*; +import com.avaje.ebean.bean.PersistenceContext.WithOption; +import com.avaje.ebean.cache.ServerCacheManager; +import com.avaje.ebean.config.EncryptKeyManager; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.config.dbplatform.DatabasePlatform; +import com.avaje.ebean.event.BeanPersistController; +import com.avaje.ebean.event.BeanQueryAdapter; +import com.avaje.ebean.meta.MetaInfoManager; +import com.avaje.ebean.text.csv.CsvReader; +import com.avaje.ebean.text.json.JsonContext; +import com.avaje.ebeaninternal.api.*; +import com.avaje.ebeaninternal.api.SpiQuery.Mode; +import com.avaje.ebeaninternal.api.SpiQuery.Type; +import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; +import com.avaje.ebeaninternal.server.ddl.DdlGenerator; +import com.avaje.ebeaninternal.server.deploy.*; +import com.avaje.ebeaninternal.server.el.ElFilter; +import com.avaje.ebeaninternal.server.jmx.MAdminAutofetch; +import com.avaje.ebeaninternal.server.lib.ShutdownManager; +import com.avaje.ebeaninternal.server.query.*; +import com.avaje.ebeaninternal.server.querydefn.DefaultOrmQuery; +import com.avaje.ebeaninternal.server.querydefn.DefaultOrmUpdate; +import com.avaje.ebeaninternal.server.querydefn.DefaultRelationalQuery; +import com.avaje.ebeaninternal.server.text.csv.TCsvReader; +import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext; +import com.avaje.ebeaninternal.server.transaction.RemoteTransactionEvent; +import com.avaje.ebeaninternal.server.transaction.TransactionManager; +import com.avaje.ebeaninternal.server.transaction.TransactionScopeManager; +import com.avaje.ebeaninternal.util.ParamTypeHelper; +import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.management.InstanceAlreadyExistsException; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.persistence.PersistenceException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.FutureTask; + +/** + * The default server side implementation of EbeanServer. + */ +public final class DefaultServer implements SpiEbeanServer { + + private static final Logger logger = LoggerFactory.getLogger(DefaultServer.class); + + private static final int IGNORE_LEADING_ELEMENTS = 5; + + private static final String AVAJE_EBEAN = Ebean.class.getName().substring(0, 15); + + private final ServerConfig serverConfig; + + private final String serverName; + + private final DatabasePlatform databasePlatform; + + private final AdminAutofetch adminAutofetch; + + private final TransactionManager transactionManager; + + private final TransactionScopeManager transactionScopeManager; + + private final int maxCallStack; + + /** + * Ebean defaults this to true but for EJB compatible behaviour set this to + * false; + */ + private final boolean rollbackOnChecked; + + /** + * Handles the save, delete, updateSql CallableSql. + */ + private final Persister persister; + + private final OrmQueryEngine queryEngine; + + private final RelationalQueryEngine relationalQueryEngine; + + private final ServerCacheManager serverCacheManager; + + private final BeanDescriptorManager beanDescriptorManager; + + private final DiffHelp diffHelp = new DiffHelp(); + + private final AutoFetchManager autoFetchManager; + + private final CQueryEngine cqueryEngine; + + private DdlGenerator ddlGenerator; + + private final ExpressionFactory expressionFactory; + + private final SpiBackgroundExecutor backgroundExecutor; + + private final DefaultBeanLoader beanLoader; + + private final EncryptKeyManager encryptKeyManager; + + private final JsonContext jsonContext; + + private final MetaInfoManager metaInfoManager; + + /** + * The MBean name used to register Ebean. + */ + private String mbeanName; + + /** + * The default PersistenceContextScope used if it is not explicitly set on a query. + */ + private final PersistenceContextScope defaultPersistenceContextScope; + + /** + * The MBeanServer Ebean is registered with. + */ + private MBeanServer mbeanServer; + + /** + * Flag set when the server has shutdown. + */ + private boolean shutdown; + + /** + * The default batch size for lazy loading beans or collections. + */ + private int lazyLoadBatchSize; + + /** + * The query batch size + */ + private int queryBatchSize; + + /** + * JDBC driver specific handling for JDBC batch execution. + */ + private PstmtBatch pstmtBatch; + + /** + * holds plugins (e.g. ddl generator) detected by the service loader + */ + private List ebeanPlugins; + + private final boolean collectQueryOrigins; + + private final boolean collectQueryStatsByNode; + + /** + * Cache used to collect statistics based on ObjectGraphNode (used to highlight lazy loading origin points). + */ + protected final ConcurrentHashMap objectGraphStats; + + /** + * Create the DefaultServer. + */ + public DefaultServer(InternalConfiguration config, ServerCacheManager cache) { + + this.serverConfig = config.getServerConfig(); + this.objectGraphStats = new ConcurrentHashMap(); + this.metaInfoManager = new DefaultMetaInfoManager(this); + this.serverCacheManager = cache; + this.pstmtBatch = config.getPstmtBatch(); + this.databasePlatform = config.getDatabasePlatform(); + this.backgroundExecutor = config.getBackgroundExecutor(); + + this.serverName = serverConfig.getName(); + this.lazyLoadBatchSize = serverConfig.getLazyLoadBatchSize(); + this.queryBatchSize = serverConfig.getQueryBatchSize(); + this.cqueryEngine = config.getCQueryEngine(); + this.expressionFactory = config.getExpressionFactory(); + this.encryptKeyManager = serverConfig.getEncryptKeyManager(); + this.defaultPersistenceContextScope = serverConfig.getPersistenceContextScope(); + + this.beanDescriptorManager = config.getBeanDescriptorManager(); + beanDescriptorManager.setEbeanServer(this); + + this.collectQueryOrigins = serverConfig.isCollectQueryOrigins(); + this.collectQueryStatsByNode = serverConfig.isCollectQueryStatsByNode(); + this.maxCallStack = serverConfig.getMaxCallStack(); + + this.rollbackOnChecked = serverConfig.isTransactionRollbackOnChecked(); + this.transactionManager = config.getTransactionManager(); + this.transactionScopeManager = config.getTransactionScopeManager(); + + this.persister = config.createPersister(this); + this.queryEngine = config.createOrmQueryEngine(); + this.relationalQueryEngine = config.createRelationalQueryEngine(); + + this.autoFetchManager = config.createAutoFetchManager(this); + this.adminAutofetch = new MAdminAutofetch(autoFetchManager); + + this.beanLoader = new DefaultBeanLoader(this); + this.jsonContext = config.createJsonContext(this); + + loadAndInitializePlugins(config); + + // Register with the JVM Shutdown hook + ShutdownManager.registerEbeanServer(this); + } + + protected void loadAndInitializePlugins(InternalConfiguration config) { + + List spiPlugins = new ArrayList(); + + for (SpiEbeanPlugin plugin : ServiceLoader.load(SpiEbeanPlugin.class)) { + spiPlugins.add(plugin); + plugin.setup(this, this.getDatabasePlatform(), config.getServerConfig()); + + if (plugin instanceof DdlGenerator) { + // backwards compatible + ddlGenerator = (DdlGenerator) plugin; + } + } + + if (ddlGenerator == null) { + // ServiceLoader not finding ddlGenerator (typically OSGi) + ddlGenerator = new DdlGenerator(); + spiPlugins.add(ddlGenerator); + ddlGenerator.setup(this, this.getDatabasePlatform(), config.getServerConfig()); + } + + ebeanPlugins = Collections.unmodifiableList(spiPlugins); + } + + public List getSpiEbeanPlugins() { + return ebeanPlugins; + } + + public void executePlugins(boolean online) { + for(SpiEbeanPlugin plugin : ebeanPlugins) { + plugin.execute(online); + } + } + + @Override + public boolean isCollectQueryOrigins() { + return collectQueryOrigins; + } + + public int getLazyLoadBatchSize() { + return lazyLoadBatchSize; + } + + public PstmtBatch getPstmtBatch() { + return pstmtBatch; + } + + public ServerConfig getServerConfig() { + return serverConfig; + } + + public DatabasePlatform getDatabasePlatform() { + return databasePlatform; + } + + @Override + public MetaInfoManager getMetaInfoManager() { + return metaInfoManager; + } + + public BackgroundExecutor getBackgroundExecutor() { + return backgroundExecutor; + } + + public ExpressionFactory getExpressionFactory() { + return expressionFactory; + } + + public DdlGenerator getDdlGenerator() { + return ddlGenerator; + } + + public AdminAutofetch getAdminAutofetch() { + return adminAutofetch; + } + + public AutoFetchManager getAutoFetchManager() { + return autoFetchManager; + } + + /** + * Run any initialisation required before registering with the ClusterManager. + */ + public void initialise() { + if (encryptKeyManager != null) { + encryptKeyManager.initialise(); + } + List> list = beanDescriptorManager.getBeanDescriptorList(); + for (int i = 0; i < list.size(); i++) { + list.get(i).cacheInitialise(); + } + + } + + /** + * Start any services after registering with the ClusterManager. + */ + public void start() { + } + + public void registerMBeans(MBeanServer mbeanServer, int uniqueServerId) { + + this.mbeanServer = mbeanServer; + this.mbeanName = "Ebean:server=" + serverName + uniqueServerId; + + ObjectName autofetchName; + try { + autofetchName = new ObjectName(mbeanName + ",key=AutoFetch"); + } catch (Exception e) { + String msg = "Failed to register the JMX beans for Ebean server [" + serverName + "]."; + logger.error(msg, e); + return; + } + + try { + mbeanServer.registerMBean(adminAutofetch, autofetchName); + + } catch (InstanceAlreadyExistsException e) { + // tomcat webapp reloading + String msg = "JMX beans for Ebean server [" + serverName + "] already registered. Will try unregister/register" + e.getMessage(); + logger.warn(msg); + try { + mbeanServer.unregisterMBean(autofetchName); + mbeanServer.registerMBean(adminAutofetch, autofetchName); + + } catch (Exception ae) { + String amsg = "Unable to unregister/register the JMX beans for Ebean server [" + serverName + "]."; + logger.error(amsg, ae); + } + } catch (Exception e) { + String msg = "Error registering MBean[" + mbeanName + "]"; + logger.error(msg, e); + } + } + + /** + * Shutting down via JVM Shutdown hook. + */ + public void shutdownManaged() { + synchronized (this) { + shutdownInternal(true, false); + } + } + + /** + * Shutting down manually. + */ + public void shutdown(boolean shutdownDataSource, boolean deregisterDriver) { + synchronized (this) { + // Unregister from JVM Shutdown hook + ShutdownManager.unregisterEbeanServer(this); + shutdownInternal(shutdownDataSource, deregisterDriver); + } + } + + /** + * Shutdown the services like threads and DataSource. + */ + private void shutdownInternal(boolean shutdownDataSource, boolean deregisterDriver) { + + logger.debug("Shutting down EbeanServer " + getName()); + if (shutdown) { + // Already shutdown + return; + } + try { + if (mbeanServer != null) { + mbeanServer.unregisterMBean(new ObjectName(mbeanName + ",key=AutoFetch")); + } + } catch (Exception e) { + logger.error("Error unregistering Ebean " + mbeanName, e); + } + + // shutdown autofetch profile collection + autoFetchManager.shutdown(); + // shutdown background threads + backgroundExecutor.shutdown(); + // shutdown DataSource (if its an Ebean one) + transactionManager.shutdown(shutdownDataSource, deregisterDriver); + shutdown = true; + } + + /** + * Return the server name. + */ + public String getName() { + return serverName; + } + + public BeanState getBeanState(Object bean) { + if (bean instanceof EntityBean) { + return new DefaultBeanState((EntityBean) bean); + } + // Not an entity bean + return null; + } + + /** + * Run the cache warming queries on all beans that have them defined. + */ + public void runCacheWarming() { + List> descList = beanDescriptorManager.getBeanDescriptorList(); + for (int i = 0; i < descList.size(); i++) { + descList.get(i).runCacheWarming(); + } + } + + public void runCacheWarming(Class beanType) { + BeanDescriptor desc = beanDescriptorManager.getBeanDescriptor(beanType); + if (desc == null) { + String msg = "Is " + beanType + " an entity? Could not find a BeanDescriptor"; + throw new PersistenceException(msg); + } else { + desc.runCacheWarming(); + } + } + + /** + * Compile a query. Only valid for ORM queries. + */ + public CQuery compileQuery(Query query, Transaction t) { + SpiOrmQueryRequest qr = createQueryRequest(Type.SUBQUERY, query, t); + OrmQueryRequest orm = (OrmQueryRequest) qr; + return cqueryEngine.buildQuery(orm); + } + + public CQueryEngine getQueryEngine() { + return cqueryEngine; + } + + public ServerCacheManager getServerCacheManager() { + return serverCacheManager; + } + + public void refreshMany(Object parentBean, String propertyName, Transaction t) { + + beanLoader.refreshMany(checkEntityBean(parentBean), propertyName, t); + } + + public void refreshMany(Object parentBean, String propertyName) { + + beanLoader.refreshMany(checkEntityBean(parentBean), propertyName); + } + + public void loadMany(LoadManyRequest loadRequest) { + + beanLoader.loadMany(loadRequest); + } + + public void loadMany(BeanCollection bc, boolean onlyIds) { + + beanLoader.loadMany(bc, onlyIds); + } + + public void refresh(Object bean) { + + beanLoader.refresh(checkEntityBean(bean)); + } + + public void loadBean(LoadBeanRequest loadRequest) { + + beanLoader.loadBean(loadRequest); + } + + public void loadBean(EntityBeanIntercept ebi) { + + beanLoader.loadBean(ebi); + } + + public Map diff(Object a, Object b) { + if (a == null) { + return null; + } + + BeanDescriptor desc = getBeanDescriptor(a.getClass()); + return diffHelp.diff(a, b, desc); + } + + /** + * Process committed beans from another framework or server in another + * cluster. + *

    + * This notifies this instance of the framework that beans have been committed + * externally to it. Either by another framework or clustered server. It needs + * to maintain its cache and text indexes appropriately. + *

    + */ + public void externalModification(TransactionEventTable tableEvent) { + SpiTransaction t = transactionScopeManager.get(); + if (t != null) { + t.getEvent().add(tableEvent); + } else { + transactionManager.externalModification(tableEvent); + } + } + + /** + * Developer informing eBean that tables where modified outside of eBean. + * Invalidate the cache etc as required. + */ + public void externalModification(String tableName, boolean inserts, boolean updates, boolean deletes) { + + TransactionEventTable evt = new TransactionEventTable(); + evt.add(tableName, inserts, updates, deletes); + + externalModification(evt); + } + + /** + * Clear the query execution statistics. + */ + public void clearQueryStatistics() { + for (BeanDescriptor desc : getBeanDescriptors()) { + desc.clearQueryStatistics(); + } + } + + /** + * Create a new EntityBean bean. + *

    + * This will generally return a subclass of the parameter 'type' which + * additionally implements the EntityBean interface. That is, the returned + * bean is typically an instance of a dynamically generated class. + *

    + */ + @SuppressWarnings("unchecked") + public T createEntityBean(Class type) { + BeanDescriptor desc = getBeanDescriptor(type); + return (T) desc.createEntityBean(); + } + + /** + * Return a Reference bean. + *

    + * If a current transaction is active then this will check the Context of that + * transaction to see if the bean is already loaded. If it is already loaded + * then it will returned that object. + *

    + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public T getReference(Class type, Object id) { + + if (id == null) { + throw new NullPointerException("The id is null"); + } + + BeanDescriptor desc = getBeanDescriptor(type); + // convert the id type if necessary + id = desc.convertId(id); + + Object ref = null; + PersistenceContext ctx = null; + + SpiTransaction t = transactionScopeManager.get(); + if (t != null) { + // first try the persistence context + ctx = t.getPersistenceContext(); + ref = ctx.get(type, id); + } + + if (ref == null) { + InheritInfo inheritInfo = desc.getInheritInfo(); + if (inheritInfo != null) { + // we actually need to do a query because + // we don't know the type without the + // discriminator value + BeanProperty idProp = desc.getIdProperty(); + if (idProp == null) { + throw new PersistenceException("No ID properties for this type? " + desc); + } + + // just select the id properties and + // the discriminator column (auto added) + Query query = createQuery(type); + query.select(idProp.getName()).setId(id); + + ref = query.findUnique(); + + } else { + // use the default reference options + ref = desc.createReference(null, id); + } + + if (ctx != null && (ref instanceof EntityBean)) { + // Not putting a vanilla reference in the persistence context + ctx.put(id, ref); + } + } + return (T) ref; + } + + @Override + public void register(TransactionCallback transactionCallback) { + Transaction transaction = currentTransaction(); + if (transaction == null) { + throw new PersistenceException("Not currently active transaction when trying to register transactionCallback"); + } + transaction.register(transactionCallback); + } + + /** + * Creates a new Transaction that is NOT stored in TransactionThreadLocal. Use + * this when you want a thread to have a second independent transaction. + */ + public Transaction createTransaction() { + + return transactionManager.createTransaction(true, -1); + } + + /** + * Create a transaction additionally specify the Isolation level. + *

    + * Note that this transaction is not stored in a thread local. + *

    + */ + public Transaction createTransaction(TxIsolation isolation) { + + return transactionManager.createTransaction(true, isolation.getLevel()); + } + + public T execute(TxCallable c) { + return execute(null, c); + } + + public T execute(TxScope scope, TxCallable c) { + ScopeTrans scopeTrans = createScopeTrans(scope); + try { + return c.call(); + + } catch (Error e) { + throw scopeTrans.caughtError(e); + + } catch (RuntimeException e) { + throw scopeTrans.caughtThrowable(e); + + } finally { + scopeTrans.onFinally(); + } + } + + public void execute(TxRunnable r) { + execute(null, r); + } + + public void execute(TxScope scope, TxRunnable r) { + ScopeTrans scopeTrans = createScopeTrans(scope); + try { + r.run(); + + } catch (Error e) { + throw scopeTrans.caughtError(e); + + } catch (RuntimeException e) { + throw scopeTrans.caughtThrowable(e); + + } finally { + scopeTrans.onFinally(); + } + } + + /** + * Determine whether to create a new transaction or not. + *

    + * This will also potentially throw exceptions for MANDATORY and NEVER types. + *

    + */ + private boolean createNewTransaction(SpiTransaction t, TxScope scope) { + + TxType type = scope.getType(); + switch (type) { + case REQUIRED: + return t == null; + + case REQUIRES_NEW: + return true; + + case MANDATORY: + if (t == null) { + throw new PersistenceException("Transaction missing when MANDATORY"); + } + return true; + + case NEVER: + if (t != null) { + throw new PersistenceException("Transaction exists for Transactional NEVER"); + } + return false; + + case SUPPORTS: + return false; + + case NOT_SUPPORTED: + throw new RuntimeException("NOT_SUPPORTED should already be handled?"); + + default: + throw new RuntimeException("Should never get here?"); + } + } + + public ScopeTrans createScopeTrans(TxScope txScope) { + + if (txScope == null) { + // create a TxScope with default settings + txScope = new TxScope(); + } + + SpiTransaction suspended = null; + + // get current transaction from ThreadLocal or equivalent + SpiTransaction t = transactionScopeManager.get(); + + boolean newTransaction; + if (txScope.getType().equals(TxType.NOT_SUPPORTED)) { + // Suspend existing transaction and + // run without a transaction in scope + newTransaction = false; + suspended = t; + t = null; + + } else { + // create a new Transaction based on TxType and t + newTransaction = createNewTransaction(t, txScope); + + if (newTransaction) { + // suspend existing transaction (if there is one) + suspended = t; + + // create a new transaction + int isoLevel = -1; + TxIsolation isolation = txScope.getIsolation(); + if (isolation != null) { + isoLevel = isolation.getLevel(); + } + t = transactionManager.createTransaction(true, isoLevel); + } + } + + // replace the current transaction ... ScopeTrans.onFinally() + // has the job of restoring the suspended transaction + transactionScopeManager.replace(t); + + return new ScopeTrans(rollbackOnChecked, newTransaction, t, txScope, suspended, transactionScopeManager); + } + + /** + * Returns the current transaction (or null) from the scope. + */ + public SpiTransaction getCurrentServerTransaction() { + return transactionScopeManager.get(); + } + + /** + * Start a transaction. + *

    + * Note that the transaction is stored in a ThreadLocal variable. + *

    + */ + public Transaction beginTransaction() { + // start an explicit transaction + SpiTransaction t = transactionManager.createTransaction(true, -1); + transactionScopeManager.set(t); + return t; + } + + public Transaction beginTransaction(TxScope scope) { + ScopeTrans scopeTrans = createScopeTrans(scope); + return new ScopedTransaction(scopeTrans); + } + + /** + * Start a transaction with a specific Isolation Level. + *

    + * Note that the transaction is stored in a ThreadLocal variable. + *

    + */ + public Transaction beginTransaction(TxIsolation isolation) { + // start an explicit transaction + SpiTransaction t = transactionManager.createTransaction(true, isolation.getLevel()); + transactionScopeManager.set(t); + return t; + } + + /** + * Return the current transaction or null if there is not one currently in + * scope. + */ + public Transaction currentTransaction() { + return transactionScopeManager.get(); + } + + /** + * Commit the current transaction. + */ + public void commitTransaction() { + transactionScopeManager.commit(); + } + + /** + * Rollback the current transaction. + */ + public void rollbackTransaction() { + transactionScopeManager.rollback(); + } + + /** + * If the current transaction has already been committed do nothing otherwise + * rollback the transaction. + *

    + * Useful to put in a finally block to ensure the transaction is ended, rather + * than a rollbackTransaction() in each catch block. + *

    + *

    + * Code example:
    + * + *

    +   * <code>
    +   * Ebean.startTransaction();
    +   * try {
    +   * 	// do some fetching and or persisting
    +   * 
    +   * 	// commit at the end
    +   * 	Ebean.commitTransaction();
    +   * 
    +   * } finally {
    +   * 	// if commit didn't occur then rollback the transaction
    +   * 	Ebean.endTransaction();
    +   * }
    +   * </code>
    +   * 
    + * + *

    + */ + public void endTransaction() { + transactionScopeManager.end(); + } + + /** + * return the next unique identity value. + *

    + * Uses the BeanDescriptor deployment information to determine the sequence to + * use. + *

    + */ + public Object nextId(Class beanType) { + BeanDescriptor desc = getBeanDescriptor(beanType); + return desc.nextId(null); + } + + @SuppressWarnings("unchecked") + public void sort(List list, String sortByClause) { + + if (list == null) { + throw new NullPointerException("list is null"); + } + if (sortByClause == null) { + throw new NullPointerException("sortByClause is null"); + } + if (list.size() == 0) { + // don't need to sort an empty list + return; + } + // use first bean in the list as the correct type + Class beanType = (Class) list.get(0).getClass(); + BeanDescriptor beanDescriptor = getBeanDescriptor(beanType); + if (beanDescriptor == null) { + String m = "BeanDescriptor not found, is [" + beanType + "] an entity bean?"; + throw new PersistenceException(m); + } + beanDescriptor.sort(list, sortByClause); + } + + public Query createQuery(Class beanType) throws PersistenceException { + return createQuery(beanType, null); + } + + public Query createNamedQuery(Class beanType, String namedQuery) throws PersistenceException { + + BeanDescriptor desc = getBeanDescriptor(beanType); + if (desc == null) { + throw new PersistenceException("Is " + beanType.getName() + " an Entity Bean? BeanDescriptor not found?"); + } + DeployNamedQuery deployQuery = desc.getNamedQuery(namedQuery); + if (deployQuery == null) { + throw new PersistenceException("named query " + namedQuery + " was not found for " + desc.getFullName()); + } + + // this will parse the query + return new DefaultOrmQuery(beanType, this, expressionFactory, deployQuery); + } + + public Filter filter(Class beanType) { + BeanDescriptor desc = getBeanDescriptor(beanType); + if (desc == null) { + String m = beanType.getName() + " is NOT an Entity Bean registered with this server?"; + throw new PersistenceException(m); + } + return new ElFilter(desc); + } + + public CsvReader createCsvReader(Class beanType) { + BeanDescriptor descriptor = getBeanDescriptor(beanType); + if (descriptor == null) { + throw new NullPointerException("BeanDescriptor for " + beanType.getName() + " not found"); + } + return new TCsvReader(this, descriptor); + } + + public Query find(Class beanType) { + return createQuery(beanType); + } + + public Query createQuery(Class beanType, String query) { + BeanDescriptor desc = getBeanDescriptor(beanType); + if (desc == null) { + String m = beanType.getName() + " is NOT an Entity Bean registered with this server?"; + throw new PersistenceException(m); + } + switch (desc.getEntityType()) { + case SQL: + if (query != null) { + throw new PersistenceException("You must used Named queries for this Entity " + desc.getFullName()); + } + // use the "default" SqlSelect + DeployNamedQuery defaultSqlSelect = desc.getNamedQuery("default"); + return new DefaultOrmQuery(beanType, this, expressionFactory, defaultSqlSelect); + + default: + return new DefaultOrmQuery(beanType, this, expressionFactory, query); + } + } + + public Update createNamedUpdate(Class beanType, String namedUpdate) { + BeanDescriptor desc = getBeanDescriptor(beanType); + if (desc == null) { + String m = beanType.getName() + " is NOT an Entity Bean registered with this server?"; + throw new PersistenceException(m); + } + + DeployNamedUpdate deployUpdate = desc.getNamedUpdate(namedUpdate); + if (deployUpdate == null) { + throw new PersistenceException("named update " + namedUpdate + " was not found for " + desc.getFullName()); + } + + return new DefaultOrmUpdate(beanType, this, desc.getBaseTable(), deployUpdate); + } + + public Update createUpdate(Class beanType, String ormUpdate) { + BeanDescriptor desc = getBeanDescriptor(beanType); + if (desc == null) { + String m = beanType.getName() + " is NOT an Entity Bean registered with this server?"; + throw new PersistenceException(m); + } + + return new DefaultOrmUpdate(beanType, this, desc.getBaseTable(), ormUpdate); + } + + public SqlQuery createSqlQuery(String sql) { + return new DefaultRelationalQuery(this, sql); + } + + public SqlQuery createNamedSqlQuery(String namedQuery) { + DNativeQuery nq = beanDescriptorManager.getNativeQuery(namedQuery); + if (nq == null) { + throw new PersistenceException("SqlQuery " + namedQuery + " not found."); + } + return new DefaultRelationalQuery(this, nq.getQuery()); + } + + public SqlUpdate createSqlUpdate(String sql) { + return new DefaultSqlUpdate(this, sql); + } + + public CallableSql createCallableSql(String sql) { + return new DefaultCallableSql(this, sql); + } + + public SqlUpdate createNamedSqlUpdate(String namedQuery) { + DNativeQuery nq = beanDescriptorManager.getNativeQuery(namedQuery); + if (nq == null) { + throw new PersistenceException("SqlUpdate " + namedQuery + " not found."); + } + return new DefaultSqlUpdate(this, nq.getQuery()); + } + + public T find(Class beanType, Object uid) { + + return find(beanType, uid, null); + } + + /** + * Find a bean using its unique id. + */ + public T find(Class beanType, Object id, Transaction t) { + + if (id == null) { + throw new NullPointerException("The id is null"); + } + + Query query = createQuery(beanType).setId(id); + return findId(query, t); + } + + private SpiOrmQueryRequest createQueryRequest(Type type, Query query, Transaction t) { + + SpiQuery spiQuery = (SpiQuery) query; + spiQuery.setType(type); + + BeanDescriptor desc = beanDescriptorManager.getBeanDescriptor(spiQuery.getBeanType()); + spiQuery.setBeanDescriptor(desc); + + return createQueryRequest(desc, spiQuery, t); + } + + public SpiOrmQueryRequest createQueryRequest(BeanDescriptor desc, SpiQuery query, Transaction t) { + + if (desc.isAutoFetchTunable() && !query.isSqlSelect() && !autoFetchManager.tuneQuery(query)) { + // use deployment FetchType.LAZY/EAGER annotations + // to define the 'default' select clause + query.setDefaultSelectClause(); + } + + if (query.selectAllForLazyLoadProperty()) { + // we need to select all properties to ensure the lazy load property + // was included (was not included by default or via autofetch). + if (logger.isDebugEnabled()) { + logger.debug("Using selectAllForLazyLoadProperty"); + } + } + + // if determine cost and no origin for Autofetch + if (query.getParentNode() == null) { + query.setOrigin(createCallStack()); + } + + // determine extra joins required to support where clause + // predicates on *ToMany properties + if (query.initManyWhereJoins()) { + // we need a sql distinct now + query.setSqlDistinct(true); + } + + boolean allowOneManyFetch = true; + if (Mode.LAZYLOAD_MANY.equals(query.getMode())) { + allowOneManyFetch = false; + + } else if (query.hasMaxRowsOrFirstRow() && !query.isRawSql() && !query.isSqlSelect()) { + // convert ALL fetch joins to Many's to be query joins + // so that limit offset type SQL clauses work + allowOneManyFetch = false; + } + + query.convertManyFetchJoinsToQueryJoins(allowOneManyFetch, queryBatchSize); + + SpiTransaction serverTrans = (SpiTransaction) t; + OrmQueryRequest request = new OrmQueryRequest(this, queryEngine, query, desc, serverTrans); + + BeanQueryAdapter queryAdapter = desc.getQueryAdapter(); + if (queryAdapter != null) { + // adaption of the query probably based on the + // current user + queryAdapter.preQuery(request); + } + + // the query hash after any tuning + request.calculateQueryPlanHash(); + + return request; + } + + /** + * Try to get the object out of the persistence context. + */ + @SuppressWarnings("unchecked") + private T findIdCheckPersistenceContextAndCache(Transaction transaction, BeanDescriptor beanDescriptor, SpiQuery query) { + + SpiTransaction t = (SpiTransaction) transaction; + if (t == null) { + t = getCurrentServerTransaction(); + } + PersistenceContext context = null; + if (t != null && useTransactionPersistenceContext(query)) { + // first look in the transaction scoped persistence context + context = t.getPersistenceContext(); + if (context != null) { + WithOption o = context.getWithOption(beanDescriptor.getBeanType(), query.getId()); + if (o != null) { + if (o.isDeleted()) { + // Bean was previously deleted in the same transaction / persistence context + return null; + } + // Return the entity bean instance from the persistence context + return (T) o.getBean(); + } + } + } + + if (!beanDescriptor.calculateUseCache(query.isUseBeanCache())) { + // not using bean cache + return null; + } + + // Hit the L2 bean cache + return beanDescriptor.cacheBeanGet(query, context); + } + + /** + * Return true if transactions PersistenceContext should be used. + */ + private boolean useTransactionPersistenceContext(SpiQuery query) { + return PersistenceContextScope.TRANSACTION.equals(getPersistenceContextScope(query)); + } + + /** + * Return the PersistenceContextScope to use defined at query or server level. + */ + public PersistenceContextScope getPersistenceContextScope(SpiQuery query) { + PersistenceContextScope scope = query.getPersistenceContextScope(); + return (scope != null) ? scope : defaultPersistenceContextScope; + } + + @SuppressWarnings("unchecked") + private T findId(Query query, Transaction t) { + + SpiQuery spiQuery = (SpiQuery) query; + spiQuery.setType(Type.BEAN); + + BeanDescriptor desc = beanDescriptorManager.getBeanDescriptor(spiQuery.getBeanType()); + spiQuery.setBeanDescriptor(desc); + + if (SpiQuery.Mode.NORMAL.equals(spiQuery.getMode()) && !spiQuery.isLoadBeanCache()) { + // See if we can skip doing the fetch completely by getting the bean from the + // persistence context or the bean cache + T bean = findIdCheckPersistenceContextAndCache(t, desc, spiQuery); + if (bean != null) { + return bean; + } + } + + SpiOrmQueryRequest request = createQueryRequest(desc, spiQuery, t); + try { + request.initTransIfRequired(); + return (T) request.findId(); + + } finally { + request.endTransIfRequired(); + } + } + + public T findUnique(Query query, Transaction t) { + + // actually a find by Id type of query... + // ... perhaps with joins and cache hints? + SpiQuery q = (SpiQuery) query; + Object id = q.getId(); + if (id != null) { + return findId(query, t); + } + + BeanDescriptor desc = beanDescriptorManager.getBeanDescriptor(q.getBeanType()); + + T bean = desc.cacheNaturalKeyLookup(q, (SpiTransaction)t); + if (bean != null) { + return bean; + } + + // a query that is expected to return either 0 or 1 rows + List list = findList(query, t); + + if (list.size() == 0) { + return null; + + } else if (list.size() > 1) { + throw new PersistenceException("Unique expecting 0 or 1 rows but got [" + list.size() + "]"); + + } else { + return list.get(0); + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Set findSet(Query query, Transaction t) { + + SpiOrmQueryRequest request = createQueryRequest(Type.SET, query, t); + + Object result = request.getFromQueryCache(); + if (result != null) { + return (Set) result; + } + + try { + request.initTransIfRequired(); + return (Set) request.findSet(); + + } finally { + request.endTransIfRequired(); + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Map findMap(Query query, Transaction t) { + + SpiOrmQueryRequest request = createQueryRequest(Type.MAP, query, t); + + Object result = request.getFromQueryCache(); + if (result != null) { + return (Map) result; + } + + try { + request.initTransIfRequired(); + return (Map) request.findMap(); + + } finally { + request.endTransIfRequired(); + } + } + + public int findRowCount(Query query, Transaction t) { + + SpiQuery copy = ((SpiQuery) query).copy(); + return findRowCountWithCopy(copy, t); + } + + public int findRowCountWithCopy(Query query, Transaction t) { + + SpiOrmQueryRequest request = createQueryRequest(Type.ROWCOUNT, query, t); + try { + request.initTransIfRequired(); + return request.findRowCount(); + + } finally { + request.endTransIfRequired(); + } + } + + public List findIds(Query query, Transaction t) { + + SpiQuery copy = ((SpiQuery) query).copy(); + + return findIdsWithCopy(copy, t); + } + + public List findIdsWithCopy(Query query, Transaction t) { + + SpiOrmQueryRequest request = createQueryRequest(Type.ID_LIST, query, t); + try { + request.initTransIfRequired(); + return request.findIds(); + + } finally { + request.endTransIfRequired(); + } + } + + public FutureRowCount findFutureRowCount(Query q, Transaction t) { + + SpiQuery copy = ((SpiQuery) q).copy(); + copy.setFutureFetch(true); + + Transaction newTxn = createTransaction(); + + CallableQueryRowCount call = new CallableQueryRowCount(this, copy, newTxn); + + QueryFutureRowCount queryFuture = new QueryFutureRowCount(call); + backgroundExecutor.execute(queryFuture.getFutureTask()); + + return queryFuture; + } + + 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); + QueryFutureIds queryFuture = new QueryFutureIds(call); + + backgroundExecutor.execute(queryFuture.getFutureTask()); + + return queryFuture; + } + + public FutureList findFutureList(Query query, Transaction t) { + + SpiQuery spiQuery = (SpiQuery) query; + spiQuery.setFutureFetch(true); + + // FutureList query always run in it's own persistence content + spiQuery.setPersistenceContext(new DefaultPersistenceContext()); + + // Create a new transaction solely to execute the findList() at some future time + Transaction newTxn = createTransaction(); + CallableQueryList call = new CallableQueryList(this, spiQuery, newTxn); + QueryFutureList queryFuture = new QueryFutureList(call); + backgroundExecutor.execute(queryFuture.getFutureTask()); + return queryFuture; + } + + @Override + public PagedList findPagedList(Query query, Transaction transaction, int pageIndex, int pageSize) { + + return new LimitOffsetPagedList(this, (SpiQuery)query, pageIndex, pageSize); + } + + public void findVisit(Query query, QueryResultVisitor visitor, Transaction t) { + + SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, t); + + request.initTransIfRequired(); + request.findVisit(visitor); + // no try finally - findVisit guarantee's cleanup of the transaction if required + } + + public void findEach(Query query, QueryEachConsumer consumer, Transaction t) { + + SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, t); + + request.initTransIfRequired(); + request.findEach(consumer); + // no try finally - findVisit guarantee's cleanup of the transaction if required + } + + public void findEachWhile(Query query, QueryEachWhileConsumer consumer, Transaction t) { + + SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, t); + + request.initTransIfRequired(); + request.findEachWhile(consumer); + // no try finally - findVisit guarantee's cleanup of the transaction if required + } + + public QueryIterator findIterate(Query query, Transaction t) { + + SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, t); + + try { + request.initTransIfRequired(); + return request.findIterate(); + + } catch (RuntimeException ex) { + request.endTransIfRequired(); + throw ex; + } + } + + @SuppressWarnings("unchecked") + public List findList(Query query, Transaction t) { + + SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, t); + + Object result = request.getFromQueryCache(); + if (result != null) { + return (List) result; + } + + try { + request.initTransIfRequired(); + return request.findList(); + + } finally { + request.endTransIfRequired(); + } + } + + public SqlRow findUnique(SqlQuery query, Transaction t) { + + // no findId() method for SqlQuery... + // a query that is expected to return either 0 or 1 rows + List list = findList(query, t); + + if (list.size() == 0) { + return null; + + } else if (list.size() > 1) { + String m = "Unique expecting 0 or 1 rows but got [" + list.size() + "]"; + throw new PersistenceException(m); + + } else { + return list.get(0); + } + } + + public SqlFutureList findFutureList(SqlQuery query, Transaction t) { + + SpiSqlQuery spiQuery = (SpiSqlQuery) query; + spiQuery.setFutureFetch(true); + + Transaction newTxn = createTransaction(); + CallableSqlQueryList call = new CallableSqlQueryList(this, query, newTxn); + + FutureTask> futureTask = new FutureTask>(call); + + backgroundExecutor.execute(futureTask); + + return new SqlQueryFutureList(query, futureTask); + } + + public List findList(SqlQuery query, Transaction t) { + + RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t); + + try { + request.initTransIfRequired(); + return request.findList(); + + } finally { + request.endTransIfRequired(); + } + } + + public Set findSet(SqlQuery query, Transaction t) { + + RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t); + + try { + request.initTransIfRequired(); + return request.findSet(); + + } finally { + request.endTransIfRequired(); + } + } + + public Map findMap(SqlQuery query, Transaction t) { + + RelationalQueryRequest request = new RelationalQueryRequest(this, relationalQueryEngine, query, t); + try { + request.initTransIfRequired(); + return request.findMap(); + + } finally { + request.endTransIfRequired(); + } + } + + /** + * Persist the bean by either performing an insert or update. + */ + public void save(Object bean) { + save(bean, null); + } + + /** + * Save the bean with an explicit transaction. + */ + public void save(Object bean, Transaction t) { + persister.save(checkEntityBean(bean), t); + } + + + @Override + public void markAsDirty(Object bean) { + if (!(bean instanceof EntityBean)) { + throw new IllegalArgumentException("This bean is not an EntityBean?"); + } + // mark the bean as dirty (so that an update will not get skipped) + ((EntityBean)bean)._ebean_getIntercept().setDirty(true); + } + + /** + * Update the bean using the default 'updatesDeleteMissingChildren' setting. + */ + public void update(Object bean) { + update(bean, null); + } + + /** + * Update the bean using the default 'updatesDeleteMissingChildren' setting. + */ + public void update(Object bean, Transaction t) { + persister.update(checkEntityBean(bean), t); + } + + /** + * Update the bean specifying the deleteMissingChildren option. + */ + public void update(Object bean, Transaction t, boolean deleteMissingChildren) { + persister.update(checkEntityBean(bean), t, deleteMissingChildren); + } + + /** + * Update all beans in the collection. + */ + public void update(Collection beans) { + update(beans, null); + } + + /** + * Update all beans in the collection with an explicit transaction. + */ + public void update(Collection beans, Transaction t) { + + if (beans == null || beans.isEmpty()) { + // Nothing to update? + return; + } + + TransWrapper wrap = initTransIfRequired(t); + try { + SpiTransaction trans = wrap.transaction; + for (Object bean : beans) { + update(checkEntityBean(bean), trans); + } + wrap.commitIfCreated(); + + } catch (RuntimeException e) { + wrap.rollbackIfCreated(); + throw e; + } + } + + /** + * Insert the bean. + */ + public void insert(Object bean) { + insert(bean, null); + } + + /** + * Insert the bean with a transaction. + */ + public void insert(Object bean, Transaction t) { + persister.insert(checkEntityBean(bean), t); + } + + /** + * Insert all beans in the collection. + */ + public void insert(Collection beans) { + insert(beans, null); + } + + /** + * Insert all beans in the collection with a transaction. + */ + public void insert(Collection beans, Transaction t) { + + if (beans == null || beans.isEmpty()) { + // Nothing to insert? + return; + } + + TransWrapper wrap = initTransIfRequired(t); + try { + SpiTransaction trans = wrap.transaction; + for (Object bean : beans) { + persister.insert(checkEntityBean(bean), trans); + } + wrap.commitIfCreated(); + + } catch (RuntimeException e) { + wrap.rollbackIfCreated(); + throw e; + } + } + + private EntityBean checkEntityBean(Object bean) { + if (bean == null) { + throw new IllegalArgumentException(Message.msg("bean.isnull")); + } + if (!(bean instanceof EntityBean)) { + throw new IllegalArgumentException("Was expecting an EntityBean but got a "+bean.getClass()); + } + return (EntityBean)bean; + } + + /** + * Delete the associations (from the intersection table) of a ManyToMany given + * the owner bean and the propertyName of the ManyToMany collection. + *

    + * This returns the number of associations deleted. + *

    + */ + public int deleteManyToManyAssociations(Object ownerBean, String propertyName) { + return deleteManyToManyAssociations(ownerBean, propertyName, null); + } + + /** + * Delete the associations (from the intersection table) of a ManyToMany given + * the owner bean and the propertyName of the ManyToMany collection. + *

    + * This returns the number of associations deleted. + *

    + */ + public int deleteManyToManyAssociations(Object ownerBean, String propertyName, Transaction t) { + + EntityBean owner = checkEntityBean(ownerBean); + TransWrapper wrap = initTransIfRequired(t); + try { + SpiTransaction trans = wrap.transaction; + int rc = persister.deleteManyToManyAssociations(owner, propertyName, trans); + wrap.commitIfCreated(); + return rc; + + } catch (RuntimeException e) { + wrap.rollbackIfCreated(); + throw e; + } + } + + /** + * Save the associations of a ManyToMany given the owner bean and the + * propertyName of the ManyToMany collection. + */ + public void saveManyToManyAssociations(Object ownerBean, String propertyName) { + saveManyToManyAssociations(ownerBean, propertyName, null); + } + + /** + * Save the associations of a ManyToMany given the owner bean and the + * propertyName of the ManyToMany collection. + */ + public void saveManyToManyAssociations(Object ownerBean, String propertyName, Transaction t) { + + EntityBean owner = checkEntityBean(ownerBean); + TransWrapper wrap = initTransIfRequired(t); + try { + SpiTransaction trans = wrap.transaction; + + persister.saveManyToManyAssociations(owner, propertyName, trans); + + wrap.commitIfCreated(); + + } catch (RuntimeException e) { + wrap.rollbackIfCreated(); + throw e; + } + } + + public void saveAssociation(Object ownerBean, String propertyName) { + saveAssociation(ownerBean, propertyName, null); + } + + public void saveAssociation(Object ownerBean, String propertyName, Transaction t) { + + EntityBean owner = checkEntityBean(ownerBean); + + TransWrapper wrap = initTransIfRequired(t); + try { + SpiTransaction trans = wrap.transaction; + persister.saveAssociation(owner, propertyName, trans); + + wrap.commitIfCreated(); + + } catch (RuntimeException e) { + wrap.rollbackIfCreated(); + throw e; + } + } + + /** + * Perform an update or insert on each bean in the iterator. Returns the + * number of beans that where saved. + */ + public int save(Iterator it) { + return save(it, null); + } + + /** + * Perform an update or insert on each bean in the collection. Returns the + * number of beans that where saved. + */ + public int save(Collection c) { + return save(c.iterator(), null); + } + + /** + * Perform an update or insert on each bean in the collection. Returns the + * number of beans that where saved. + */ + public int save(Collection c, Transaction t) { + return save(c.iterator(), t); + } + + /** + * Save all beans in the iterator with an explicit transaction. + */ + public int save(Iterator it, Transaction t) { + + TransWrapper wrap = initTransIfRequired(t); + try { + wrap.batchEscalateOnCollection(); + SpiTransaction trans = wrap.transaction; + int saveCount = 0; + while (it.hasNext()) { + EntityBean bean = checkEntityBean(it.next()); + persister.save(bean, trans); + saveCount++; + } + + wrap.commitIfCreated(); + wrap.flushBatchOnCollection(); + return saveCount; + + } catch (RuntimeException e) { + wrap.rollbackIfCreated(); + throw e; + } + } + + public int delete(Class beanType, Object id) { + return delete(beanType, id, null); + } + + public int delete(Class beanType, Object id, Transaction t) { + + TransWrapper wrap = initTransIfRequired(t); + try { + SpiTransaction trans = wrap.transaction; + int rowCount = persister.delete(beanType, id, trans); + wrap.commitIfCreated(); + + return rowCount; + + } catch (RuntimeException e) { + wrap.rollbackIfCreated(); + throw e; + } + } + + public void delete(Class beanType, Collection ids) { + delete(beanType, ids, null); + } + + public void delete(Class beanType, Collection ids, Transaction t) { + + TransWrapper wrap = initTransIfRequired(t); + try { + SpiTransaction trans = wrap.transaction; + persister.deleteMany(beanType, ids, trans); + wrap.commitIfCreated(); + + } catch (RuntimeException e) { + wrap.rollbackIfCreated(); + throw e; + } + } + + /** + * Delete the bean. + */ + public void delete(Object bean) { + delete(bean, null); + } + + /** + * Delete the bean with the explicit transaction. + */ + public void delete(Object bean, Transaction t) { + + persister.delete(checkEntityBean(bean), t); + } + + /** + * Delete all the beans in the iterator. + */ + public int delete(Iterator it) { + return delete(it, null); + } + + /** + * Delete all the beans in the collection. + */ + public int delete(Collection c) { + return delete(c.iterator(), null); + } + + /** + * Delete all the beans in the iterator with an explicit transaction. + */ + public int delete(Iterator it, Transaction t) { + + TransWrapper wrap = initTransIfRequired(t); + + try { + wrap.batchEscalateOnCollection(); + SpiTransaction trans = wrap.transaction; + int deleteCount = 0; + while (it.hasNext()) { + EntityBean bean = checkEntityBean(it.next()); + persister.delete(bean, trans); + deleteCount++; + } + + wrap.commitIfCreated(); + wrap.flushBatchOnCollection(); + return deleteCount; + + } catch (RuntimeException e) { + wrap.rollbackIfCreated(); + throw e; + } + } + + /** + * Execute the CallableSql with an explicit transaction. + */ + public int execute(CallableSql callSql, Transaction t) { + return persister.executeCallable(callSql, t); + } + + /** + * Execute the CallableSql. + */ + public int execute(CallableSql callSql) { + return execute(callSql, null); + } + + /** + * Execute the updateSql with an explicit transaction. + */ + public int execute(SqlUpdate updSql, Transaction t) { + return persister.executeSqlUpdate(updSql, t); + } + + /** + * Execute the updateSql. + */ + public int execute(SqlUpdate updSql) { + return execute(updSql, null); + } + + /** + * Execute the updateSql with an explicit transaction. + */ + public int execute(Update update, Transaction t) { + return persister.executeOrmUpdate(update, t); + } + + /** + * Execute the orm update. + */ + public int execute(Update update) { + return execute(update, null); + } + + /** + * Return all the BeanDescriptors. + */ + public List> getBeanDescriptors() { + return beanDescriptorManager.getBeanDescriptorList(); + } + + public void register(BeanPersistController c) { + List> list = beanDescriptorManager.getBeanDescriptorList(); + for (int i = 0; i < list.size(); i++) { + list.get(i).register(c); + } + } + + public void deregister(BeanPersistController c) { + List> list = beanDescriptorManager.getBeanDescriptorList(); + for (int i = 0; i < list.size(); i++) { + list.get(i).deregister(c); + } + } + + public boolean isSupportedType(java.lang.reflect.Type genericType) { + + TypeInfo typeInfo = ParamTypeHelper.getTypeInfo(genericType); + return typeInfo != null && getBeanDescriptor(typeInfo.getBeanType()) != null; + } + + public Object getBeanId(Object bean) { + EntityBean eb = checkEntityBean(bean); + BeanDescriptor desc = getBeanDescriptor(bean.getClass()); + if (desc == null) { + String m = bean.getClass().getName() + " is NOT an Entity Bean registered with this server?"; + throw new PersistenceException(m); + } + + return desc.getId(eb); + } + + /** + * Return the BeanDescriptor for a given type of bean. + */ + public BeanDescriptor getBeanDescriptor(Class beanClass) { + return beanDescriptorManager.getBeanDescriptor(beanClass); + } + + /** + * Return the BeanDescriptor's for a given table name. + */ + public List> getBeanDescriptors(String tableName) { + return beanDescriptorManager.getBeanDescriptors(tableName); + } + + /** + * Return the BeanDescriptor using its unique id. + */ + public BeanDescriptor getBeanDescriptorById(String descriptorId) { + return beanDescriptorManager.getBeanDescriptorById(descriptorId); + } + + /** + * Another server in the cluster sent this event so that we can inform local + * BeanListeners of inserts updates and deletes that occurred remotely (on + * another server in the cluster). + */ + public void remoteTransactionEvent(RemoteTransactionEvent event) { + transactionManager.remoteTransactionEvent(event); + } + + /** + * Create a transaction if one is not currently active in the + * TransactionThreadLocal. + *

    + * Returns a TransWrapper which contains the wasCreated flag. If this is true + * then the transaction was created for this request in which case it will + * need to be committed after the request has been processed. + *

    + */ + TransWrapper initTransIfRequired(Transaction t) { + + if (t != null) { + return new TransWrapper((SpiTransaction) t, false); + } + + boolean wasCreated = false; + SpiTransaction trans = transactionScopeManager.get(); + if (trans == null) { + // create a transaction + trans = transactionManager.createTransaction(false, -1); + wasCreated = true; + } + return new TransWrapper(trans, wasCreated); + } + + public SpiTransaction createServerTransaction(boolean isExplicit, int isolationLevel) { + return transactionManager.createTransaction(isExplicit, isolationLevel); + } + + public SpiTransaction createQueryTransaction() { + return transactionManager.createQueryTransaction(); + } + + + /** + * Create a CallStack object. + *

    + * This trims off the avaje ebean part of the stack trace so that the first + * element in the CallStack should be application code. + *

    + */ + public CallStack createCallStack() { + + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + + // ignore the first 6 as they are always avaje stack elements + int startIndex = IGNORE_LEADING_ELEMENTS; + + // find the first non-avaje stackElement + for (; startIndex < stackTrace.length; startIndex++) { + if (!stackTrace[startIndex].getClassName().startsWith(AVAJE_EBEAN)) { + break; + } + } + + int stackLength = stackTrace.length - startIndex; + if (stackLength > maxCallStack) { + // maximum of maxCallStack stackTrace elements + stackLength = maxCallStack; + } + + // create the 'interesting' part of the stackTrace + StackTraceElement[] finalTrace = new StackTraceElement[stackLength]; + System.arraycopy(stackTrace, startIndex, finalTrace, 0, stackLength); + + if (stackLength < 1) { + // this should not really happen + throw new RuntimeException("StackTraceElement size 0? stack: " + Arrays.toString(stackTrace)); + } + + return new CallStack(finalTrace); + } + + + @Override + public JsonContext json() { + // immutable thread safe so return shared instance + return jsonContext; + } + + @Override + public JsonContext createJsonContext() { + return json(); + } + + + @Override + public void collectQueryStats(ObjectGraphNode node, long loadedBeanCount, long timeMicros) { + + if (collectQueryStatsByNode) { + CObjectGraphNodeStatistics nodeStatistics = objectGraphStats.get(node); + if (nodeStatistics == null) { + // race condition here but I actually don't care too much if we miss a + // few early statistics - especially when the server is warming up etc + nodeStatistics = new CObjectGraphNodeStatistics(node); + objectGraphStats.put(node, nodeStatistics); + } + nodeStatistics.add(loadedBeanCount, timeMicros); + } + } + +}