diff --git a/src/main/java/com/avaje/ebean/AdminAutofetch.java b/src/main/java/com/avaje/ebean/AdminAutofetch.java new file mode 100644 index 000000000..c9c7046ee --- /dev/null +++ b/src/main/java/com/avaje/ebean/AdminAutofetch.java @@ -0,0 +1,137 @@ +package com.avaje.ebean; + +/** + * Administrative control of Autofetch during runtime. + */ +public interface AdminAutofetch { + + /** + * Return true if profiling is enabled. + */ + public boolean isProfiling(); + + /** + * Set to true to enable profiling. + */ + public void setProfiling(boolean enable); + + /** + * Return true if autoFetch automatic query tuning is enabled. + */ + public boolean isQueryTuning(); + + /** + * Set to true to enable autoFetch automatic query tuning. + */ + public void setQueryTuning(boolean enable); + + /** + * Returns the rate which profiling is collected. This is an int between 0 and + * 100. + */ + public double getProfilingRate(); + + /** + * Set the rate at which profiling is collected after the base. + * + * @param rate + * a int between 0 and 100. + */ + public void setProfilingRate(double rate); + + /** + * Return the number of queries profiled after which profiling is collected at + * a percentage rate. + */ + public int getProfilingBase(); + + /** + * Set a base number of queries to profile per query point. + *
+ * After this amount of profiling has been obtained profiling is collected at + * the Profiling Percentage rate. + *
+ */ + public void setProfilingBase(int profilingBase); + + /** + * Return the minimum number of queries profiled before autoFetch will start + * automatically tuning the queries. + *+ * This could be one which means start autoFetch tuning after the first + * profiling information is collected. + *
+ */ + public int getProfilingMin(); + + /** + * Set the minimum number of queries profiled per query point before autoFetch + * will automatically tune the queries. + *+ * Increasing this number will mean more profiling is collected before + * autoFetch starts tuning the query. + *
+ */ + public 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(); + + /** + * This will take the current profiling information and update the "tuned + * query detail". + *+ * This is done periodically and can also be manually invoked. + *
+ * + * @return a summary of the updates that occurred + */ + public String updateTunedQueryInfo(); + + /** + * Clear all the tuned query info. + *+ * Should only need do this for testing and playing around. + *
+ * + * @return the amount of tuned query information cleared. + */ + public int clearTunedQueryInfo(); + + /** + * Clear all the profiling information. + *+ * This means the profiling information will need to be re-gathered. + *
+ *+ * Should only need do this for testing and playing around. + *
+ * + * @return the amount of profiled information cleared. + */ + public int clearProfilingInfo(); + + /** + * Clear the query execution statistics. + */ + public void clearQueryStatistics(); + + /** + * Return the number of queries tuned by AutoFetch. + */ + public int getTotalTunedQueryCount(); + + /** + * Return the size of the TuneQuery map. + */ + public int getTotalTunedQuerySize(); + + /** + * Return the size of the profile map. + */ + public 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 new file mode 100644 index 000000000..f3b86b79b --- /dev/null +++ b/src/main/java/com/avaje/ebean/BackgroundExecutor.java @@ -0,0 +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); +} diff --git a/src/main/java/com/avaje/ebean/BeanState.java b/src/main/java/com/avaje/ebean/BeanState.java new file mode 100644 index 000000000..fc4e3c371 --- /dev/null +++ b/src/main/java/com/avaje/ebean/BeanState.java @@ -0,0 +1,94 @@ +package com.avaje.ebean; + +import java.beans.PropertyChangeListener; +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+ * 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 reference object. + *+ * You can create a new EntityBean ( + * {@link EbeanServer#createEntityBean(Class)}, set its Id property and then + * call this setReference() method. + *
+ */ + public void setReference(); + + /** + * 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. + * + * @param loadedProperties + * the properties that where loaded or null for a fully loaded entity + * bean. + */ + public void setLoaded(Set+ * Note that UpdateSql is designed for general DML sql and CallableSql is + * designed for use with stored procedures. Also note that when using this in + * batch mode the out parameters are not read. + *
+ *+ * Example 1: + *
+ * + *
+ * String sql = "{call sp_order_mod(?,?)}";
+ *
+ * CallableSql cs = Ebean.createCallableSql(sql);
+ * cs.setParameter(1, "turbo");
+ * cs.registerOut(2, Types.INTEGER);
+ *
+ * Ebean.execute(cs);
+ *
+ * // read the out parameter
+ * Integer returnValue = (Integer) cs.getObject(2);
+ *
+ *
+ *
+ * Example 2:
+ * Includes batch mode, table modification information and label. Note that the
+ * label is really only to help people reading the transaction logs to identify
+ * the procedure called etc.
+ *
+ * String sql = "{call sp_insert_order(?,?)}";
+ *
+ * CallableSql cs = Ebean.createCallableSql(sql);
+ *
+ * // Inform Ebean this stored procedure inserts into the
+ * // oe_order table and inserts + updates the oe_order_detail table.
+ * // this is used to invalidate objects in the cache
+ * cs.addModification("oe_order", true, false, false);
+ * cs.addModification("oe_order_detail", true, true, false);
+ *
+ * Transaction t = Ebean.startTransaction();
+ *
+ * // execute using JDBC batching 10 statements at a time
+ * t.setBatchMode(true);
+ * t.setBatchSize(10);
+ * try {
+ * cs.setParameter(1, "Was");
+ * cs.setParameter(2, "Banana");
+ * Ebean.execute(cs);
+ *
+ * cs.setParameter(1, "Here");
+ * cs.setParameter(2, "Kumera");
+ * Ebean.execute(cs);
+ *
+ * cs.setParameter(1, "More");
+ * cs.setParameter(2, "Apple");
+ * Ebean.execute(cs);
+ *
+ * // Ebean.externalModification("oe_order",true,false,false);
+ * // Ebean.externalModification("oe_order_detail",true,true,false);
+ * Ebean.commitTransaction();
+ *
+ * } finally {
+ * Ebean.endTransaction();
+ * }
+ *
+ *
+ * @see com.avaje.ebean.SqlUpdate
+ * @see com.avaje.ebean.Ebean#execute(CallableSql)
+ */
+public interface CallableSql {
+
+ /**
+ * Return the label that is put into the transaction log.
+ */
+ public String getLabel();
+
+ /**
+ * Set the label that is put in the transaction log.
+ */
+ public CallableSql setLabel(String label);
+
+ /**
+ * Return the statement execution timeout.
+ */
+ public int getTimeout();
+
+ /**
+ * Return the callable sql.
+ */
+ public String getSql();
+
+ /**
+ * Set the statement execution timeout. Zero implies unlimited time.
+ * + * This is set to the underlying CallableStatement. + *
+ */ + public CallableSql setTimeout(int secs); + + /** + * Set the callable sql. + */ + public CallableSql setSql(String sql); + + /** + * Bind a parameter that is bound as a IN parameter. + *+ * position starts at value 1 (not 0) to be consistent with CallableStatement. + *
+ *+ * This is designed so that you do not need to set params in index order. You + * can set/register param 2 before param 1 etc. + *
+ * + * @param position + * the index position of the parameter. + * @param value + * the value of the parameter. + */ + public CallableSql bind(int position, Object value); + + /** + * Bind a positioned parameter (same as bind method). + * + * @param position + * the index position of the parameter. + * @param value + * the value of the parameter. + */ + public CallableSql setParameter(int position, Object value); + + /** + * Register an OUT parameter. + *+ * Note that position starts at value 1 (not 0) to be consistent with + * CallableStatement. + *
+ *+ * This is designed so that you do not need to register params in index order. + * You can set/register param 2 before param 1 etc. + *
+ * + * @param position + * the index position of the parameter (starts with 1). + * @param type + * the jdbc type of the OUT parameter that will be read. + */ + public CallableSql registerOut(int position, int type); + + /** + * Return an OUT parameter value. + *+ * position starts at value 1 (not 0) to be consistent with CallableStatement. + *
+ *+ * This can only be called after the CallableSql has been executed. When run + * in batch mode you effectively can't use this method. + *
+ */ + public Object getObject(int position); + + /** + * + * You can extend this object and override this method for more advanced + * stored procedure calls. This would be the case when ResultSets are returned + * etc. + */ + public boolean executeOverride(CallableStatement cstmt) throws SQLException; + + /** + * Add table modification information to the TransactionEvent. + *
+ * This would be similar to using the
+ * Ebean.externalModification() method. It may be easier and make
+ * more sense to set it here with the CallableSql.
+ *
+ * For UpdateSql the table modification information is derived by parsing the + * sql to determine the table name and whether it was an insert, update or + * delete. + *
+ */ + public 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/DRawSqlColumnsParser.java b/src/main/java/com/avaje/ebean/DRawSqlColumnsParser.java new file mode 100644 index 000000000..058990467 --- /dev/null +++ b/src/main/java/com/avaje/ebean/DRawSqlColumnsParser.java @@ -0,0 +1,93 @@ +package com.avaje.ebean; + +import java.util.ArrayList; +import java.util.Arrays; + +import javax.persistence.PersistenceException; + +import com.avaje.ebean.RawSql.ColumnMapping; + +/** + * Parses columnMapping (select clause) mapping columns to bean properties. + */ +final class DRawSqlColumnsParser { + + private final int end; + + private final String sqlSelect; + + private int pos; + + private int indexPos; + + public static ColumnMapping parse(String sqlSelect) { + return new DRawSqlColumnsParser(sqlSelect).parse(); + } + + private DRawSqlColumnsParser(String sqlSelect) { + this.sqlSelect = sqlSelect; + this.end = sqlSelect.length(); + } + + private ColumnMapping parse() { + + ArrayList+ * 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. + *
+ *+ * 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. + *
+ * + *
+ * // fetch shipped orders (and also their customer)
+ * List<Order> 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();
+ * ...
+ * }
+ *
+ *
+ * + * // 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. + *
+ * + *
+ * // 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+ * 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. + *
+ * + *
+ * // 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. + */ + protected static void register(EbeanServer server, boolean isPrimaryServer) { + serverMgr.register(server, isPrimaryServer); + } + + /** + * 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. + *
+ * + *
+ * // 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); + } + + /** + * Returns the current transaction or null if there is no current transaction + * in scope. + */ + public static Transaction currentTransaction() { + return serverMgr.getPrimaryServer().currentTransaction(); + } + + /** + * 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: + *
+ * + *
+ * 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+ * 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. + *
+ * + *
+ * public class Order { ...
+ *
+ * @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
+ * @JoinColumn(name="order_id")
+ * List<OrderDetail> 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); + } + + /** + * Force an update using the bean updating the non-null properties. + *+ * You can use this method to FORCE an update to occur (even on a bean that + * has not been fetched but say built from JSON or XML). When + * {@link Ebean#save(Object)} is used Ebean determines whether to use an + * insert or an update based on the state of the bean. Using this method will + * force an update to occur. + *
+ *+ * It is expected that this method is most useful in stateless REST services + * or web applications where you have the values you wish to update but no + * existing bean. + *
+ *+ * For updates against beans that have not been fetched (say built from JSON + * or XML) this will treat deleteMissingChildren=true and will delete any + * 'missing children'. Refer to + * {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}. + *
+ * + *
+ *
+ * Customer c = new Customer();
+ * c.setId(7);
+ * c.setName("ModifiedNameNoOCC");
+ *
+ * // generally you should set the version property
+ * // so that Optimistic Concurrency Checking is used.
+ * // If a version property is not set then no Optimistic
+ * // Concurrency Checking occurs for the update
+ * // c.setLastUpdate(lastUpdateTime);
+ *
+ * // by default the Non-null properties
+ * // are included in the update
+ * Ebean.update(c);
+ *
+ *
+ */
+ public static void update(Object bean) {
+ serverMgr.getPrimaryServer().update(bean);
+ }
+
+ /**
+ * Force an update using the bean explicitly stating the properties to update.
+ * + * If you don't specify explicit properties to use in the update then the + * non-null properties are included in the update. + *
+ *+ * For updates against beans that have not been fetched (say built from JSON + * or XML) this will treat deleteMissingChildren=true and will delete any + * 'missing children'. Refer to + * {@link EbeanServer#update(Object, Set, Transaction, boolean, boolean)}. + *
+ * + * @param bean + * The bean holding the values to be included in the update. + * @param updateProps + * the explicit set of properties to include in the update (can be + * null). + */ + public static void update(Object bean, Set+ * 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 does not refresh any OneToMany or ManyToMany properties. + *
+ */ + public static void refresh(Object bean) { + serverMgr.getPrimaryServer().refresh(bean); + } + + /** + * Refresh a 'many' property of a bean. + * + *+ * 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). + *
+ * + *+ * 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
+ * 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. + *
+ * + *
+ *
+ * // find orders and their customers
+ * List<Order> 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 + * // 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(); + *
+ * + *
+ * // ... 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<Order> query = Ebean.createQuery(Order.class);
+ * query.setId(1);
+ * query.fetch("customer");
+ * query.fetch("customer.shippingAddress");
+ * query.fetch("details");
+ *
+ * // 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<OrderDetail> 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 + * 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. + *
+ * + *
+ * // 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. + *
+ * + *
+ * // example
+ * Query<Order> query = Ebean.createNamedQuery(Order.class, "new.for.customer");
+ * query.setParameter("customerId", 23);
+ * List<Order> newOrders = query.findList();
+ *
+ *
+ * @param beanType
+ * the class of entity to be fetched
+ * @param namedQuery
+ * the name of the query
+ */
+ public static + * 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)}. + *
+ * + *
+ *
+ * String q = "find order fetch details where status = :st";
+ *
+ * List<Order> newOrders = Ebean.createQuery(Order.class, q)
+ * .setParameter("st", Order.Status.NEW)
+ * .findList();
+ *
+ *
+ * @param query
+ * the object query
+ */
+ public static + * 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: + *
+ * + *
+ * 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: + *
+ * + *
+ * Update<Topic> 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 + * 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: + *
+ * + *
+ *
+ * // 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<Topic> 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 + * 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. + *
+ * + *
+ * // Find order 2 additionally fetching the customer, details and details.product
+ * // name.
+ *
+ * Query<Order> query = Ebean.createQuery(Order.class);
+ * query.fetch("customer");
+ * query.fetch("details");
+ * query.fetch("detail.product", "name");
+ * query.setId(2);
+ *
+ * Order order = query.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<Order> query = Ebean.createQuery(Order.class);
+ * query.setQuery(oql);
+ * query.setParameter("orderId", 2);
+ *
+ * Order order = query.findUnique();
+ *
+ * // Using a named query
+ * Query<Order> query = Ebean.createQuery(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 + * 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+ * 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+ * 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: + *
+ * + *
+ * // 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: + *
+ * + *
+ * 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. + *
+ * + *
+ * // 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). + *
+ * + *
+ * 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. + *
+ * + *
+ * // set specific transactional scope settings
+ * TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
+ *
+ * Ebean.execute(scope, new TxCallable<String>() {
+ * public String call() {
+ * User u1 = Ebean.find(User.class, 1);
+ * ...
+ * return u1.getEmail();
+ * }
+ * });
+ *
+ *
+ */
+ public static + * 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). + *
+ * + *
+ * 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();
+ * }
+ * });
+ *
+ */
+ public static + * 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 commited 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 (or subclassed) 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); + } + + /** + * Create a JsonContext that will use the default configuration options. + */ + public static JsonContext createJsonContext() { + return serverMgr.getPrimaryServer().createJsonContext(); + } + +} diff --git a/src/main/java/com/avaje/ebean/EbeanServer.java b/src/main/java/com/avaje/ebean/EbeanServer.java new file mode 100644 index 000000000..e31240efe --- /dev/null +++ b/src/main/java/com/avaje/ebean/EbeanServer.java @@ -0,0 +1,1154 @@ +package com.avaje.ebean; + +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.persistence.OptimisticLockException; + +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; + +/** + * Provides the API for fetching and saving beans to a particular DataSource. + *
+ * Registration with the Ebean Singleton:
+ * When a EbeanServer is constructed it can be registered with the Ebean
+ * singleton (see {@link ServerConfig#setRegister(boolean)}). The Ebean
+ * singleton is essentially a map of EbeanServer's that have been registered
+ * with it. The EbeanServer can then be retrieved later via
+ * {@link Ebean#getServer(String)}.
+ *
+ * The 'default' EbeanServer
+ * One EbeanServer can be designated as the 'default' or 'primary' EbeanServer
+ * (see {@link ServerConfig#setDefaultServer(boolean)}. Many methods on Ebean
+ * such as {@link Ebean#find(Class)} etc are actually just a convenient way to
+ * call methods on the 'default/primary' EbeanServer. This is handy for
+ * applications that use a single DataSource.
+ *
+ * There is one EbeanServer per Database (javax.sql.DataSource). One EbeanServer + * is referred to as the 'default' server and that is the one that + * Ebean methods such as {@link Ebean#find(Class)} use. + *
+ *
+ * Constructing a EbeanServer
+ * EbeanServer's are constructed by the EbeanServerFactory. They can be created
+ * programmatically via {@link EbeanServerFactory#create(ServerConfig)} or they
+ * can be automatically constructed on demand using configuration information in
+ * the ebean.properties file.
+ *
+ * Example: Get a EbeanServer + *
+ * + *
+ * // Get access to the Human Resources EbeanServer/Database
+ * EbeanServer hrServer = Ebean.getServer("HR");
+ *
+ *
+ * // fetch contact 3 from the HR database Contact contact =
+ * hrServer.find(Contact.class, new Integer(3));
+ *
+ * contact.setStatus("INACTIVE"); ...
+ *
+ * // save the contact back to the HR database hrServer.save(contact);
+ *
+ *
+ *
+ * EbeanServer has more API than Ebean
+ * EbeanServer provides additional API compared with Ebean. For example it
+ * provides more control over the use of Transactions that is not available in
+ * the Ebean API.
+ *
+ * External Transactions: If you wanted to use transactions created + * externally to eBean then EbeanServer provides additional methods where you + * can explicitly pass a transaction (that can be created externally). + *
+ *+ * Bypass ThreadLocal Mechanism: If you want to bypass the built in + * ThreadLocal transaction management you can use the createTransaction() + * method. Example: a single thread requires more than one transaction. + *
+ * + * @see Ebean + * @see EbeanServerFactory + * @see ServerConfig + */ +public interface EbeanServer { + + /** + * Return the AdminAutofetch which is used to control and configure the + * Autofetch service at runtime. + */ + public 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(); + + /** + * Return the ExpressionFactory for this server. + */ + public ExpressionFactory getExpressionFactory(); + + /** + * Return the BeanState for a given entity bean. + *+ * This will return null if the bean is not an enhanced (or subclassed) entity + * bean. + *
+ */ + public BeanState getBeanState(Object bean); + + /** + * Return the value of the Id property for a given bean. + */ + public Object getBeanId(Object bean); + + /** + * 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 Map+ * Note that if you are using enhancement (rather than subclassing) then you + * do not need to use this method and just new up a bean. + *
+ *+ * Potentially useful when using subclassing and you wish to programmatically + * load a entity bean . Otherwise this method is generally not required. + *
+ */ + public+ * This is NOT required when entity beans are "Enhanced" (via java agent or + * ant task etc). + *
+ *+ * The reason this is needed to deserialise "Proxy" beans is because Ebean + * creates the "Proxy/SubClass" classes in a class loader - and generally the + * class loader deserialising the inputStream is not aware of these other + * classes. + *
+ */ + public ObjectInputStream createProxyObjectInputStream(InputStream is); + + /** + * Create a CsvReader for a given beanType. + */ + public+ * The query statement will be defined in a deployment orm xml file. + *
+ * + * @see Ebean#createQuery(Class, String) + */ + public+ * 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)}. + *
+ * + *
+ * EbeanServer ebeanServer = ... ;
+ * String q = "find order fetch details where status = :st";
+ *
+ * List<Order> newOrders
+ * = ebeanServer.createQuery(Order.class, q)
+ * .setParameter("st", Order.Status.NEW)
+ * .findList();
+ *
+ *
+ * @param query
+ * the object query
+ */
+ public + * This will only work when a IdGenerator is on the bean such as for beans + * that use 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 Object nextId(Class> beanType); + + /** + * Create a filter for filtering lists of entity beans. + */ + public+ * The query statement will be defined in a deployment orm xml file. + *
+ * + * @see Ebean#createNamedSqlQuery(String) + */ + public SqlQuery createNamedSqlQuery(String namedQuery); + + /** + * Create a sql update for executing native dml statements (refer + * {@link Ebean#createSqlUpdate(String)}). + * + * @see Ebean#createSqlUpdate(String) + */ + public SqlUpdate createSqlUpdate(String sql); + + /** + * Create a CallableSql to execute a given stored procedure. + */ + public CallableSql createCallableSql(String callableSql); + + /** + * Create a named sql update (refer {@link Ebean#createNamedSqlUpdate(String)} + * ). + *+ * The statement (an Insert Update or Delete statement) will be defined in a + * deployment orm xml file. + *
+ * + * @see Ebean#createNamedSqlUpdate(String) + */ + public SqlUpdate createNamedSqlUpdate(String namedQuery); + + /** + * Create a new transaction that is not held in TransactionThreadLocal. + *+ * You will want to do this if you want multiple Transactions in a single + * thread or generally use transactions outside of the TransactionThreadLocal + * management. + *
+ */ + public Transaction createTransaction(); + + /** + * Create a new transaction additionally specifying the isolation level. + *+ * Note that this transaction is NOT stored in a thread local. + *
+ */ + public Transaction createTransaction(TxIsolation isolation); + + /** + * Start a new transaction putting it into a ThreadLocal. + * + * @see Ebean#beginTransaction() + */ + public Transaction beginTransaction(); + + /** + * Start a transaction additionally specifying the isolation level. + */ + public Transaction beginTransaction(TxIsolation isolation); + + /** + * Returns the current transaction or null if there is no current transaction + * in scope. + */ + public Transaction currentTransaction(); + + /** + * Commit the current transaction. + * + * @see Ebean#commitTransaction() + */ + public void commitTransaction(); + + /** + * Rollback the current transaction. + * + * @see Ebean#rollbackTransaction() + */ + public void 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: + * + *
+ * 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(); }
+ *
+ *
+ *
+ *
+ * @see Ebean#endTransaction()
+ */
+ public void endTransaction();
+
+ /**
+ * Refresh the values of a bean.
+ * + * Note that this does not refresh any OneToMany or ManyToMany properties. + *
+ * + * @see Ebean#refresh(Object) + */ + public void refresh(Object bean); + + /** + * Refresh a many property of an entity bean. + * + * @param bean + * the entity bean containing the 'many' property + * @param propertyName + * the 'many' property to be refreshed + * + * @see Ebean#refreshMany(Object, String) + */ + public void refreshMany(Object bean, String propertyName); + + /** + * Find a bean using its unique id. + * + * @see Ebean#find(Class, Object) + */ + public+ * This will not perform a query against the database. + *
+ * + * @see Ebean#getReference(Class, Object) + */ + public