package io.ebean; import io.ebean.annotation.TxIsolation; import io.ebean.cache.ServerCacheManager; import io.ebean.config.BeanNotEnhancedException; import io.ebean.config.ServerConfig; import io.ebean.datasource.DataSourceConfigurationException; import io.ebean.plugin.Property; import io.ebean.text.csv.CsvReader; import io.ebean.text.json.JsonContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.persistence.OptimisticLockException; import javax.persistence.PersistenceException; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; /** * Ebean is a registry of {@link Database} by name. Ebean has now been renamed to {@link DB}. *
* Ebean is effectively this is an alias for {@link DB} which is the new and improved name for Ebean. *
* The preference is to use DB and Database rather than Ebean and EbeanServer.
*/
public final class Ebean {
private static final Logger logger = LoggerFactory.getLogger(Ebean.class);
static {
EbeanVersion.getVersion(); // initialises the version class and logs the version.
}
/**
* Manages creation and cache of Databases.
*/
private static final Ebean.ServerManager serverMgr = new Ebean.ServerManager();
/**
* Helper class for managing fast and safe access and creation of Databases.
*/
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.
*
* This is equivalent to
* 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(..., ...);
*
* 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.
*
* With REQUIRED semantics if an active transaction already exists that transaction will be used.
*
* 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.
*
* 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.
*
* Note that this provides an try finally alternative to using {@link #executeCall(TxScope, Callable)} or
* {@link #execute(TxScope, Runnable)}.
*
*
* Flushing occurs automatically when:
*
* Useful to put in a finally block to ensure the transaction is ended, rather
* than a rollbackTransaction() in each catch block.
*
* Code example:
*
* When null is passed in for b, then the 'OldValues' of a is used for the
* difference comparison.
*
* 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.
*
* 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.
*
* 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.
*
* 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.
*
* Note: This method queries the DB for uniqueness of all indices, so do not use it in a batch update.
*
* Note: This checks only the root bean!
*
*
* This will return true if the bean was deleted successfully or JDBC batch is being used.
*
* If there is no current transaction one will be created and committed for
* you automatically.
*
* If the bean is configured with
* If the Bean does not have a version property (or loaded version property) and
* the bean does not exist then this returns false indicating that nothing was
* deleted. Note that, if JDBC batch mode is used then this always returns true.
*
* Note that this resets OneToMany and ManyToMany properties so that if they
* are accessed a lazy load will refresh the many property.
*
* This is sometimes described as a proxy (with lazy loading).
*
* 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.
*
* If you want more control over the query then you can use createQuery() and
* Query.findOne();
*
* Note that you can use raw SQL with entity beans, refer to the SqlSelect
* annotation for examples.
*
* 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.
*
* 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:
*
* For RawSql the named query is expected to be in ebean.xml.
*
* 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 findOne() 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.
*
*
* 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).
*
* The native SQL can contain named parameters or positioned parameters.
*
* DTO beans are just normal bean like classes with public constructor(s) and setters.
* They do not need to be registered with Ebean before use.
*
*
* This produces and returns a new list with the sort and filters applied.
*
* Refer to {@link Filter} for an example of its use.
*
* 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:
*
* The scope can control the transaction type, isolation and rollback
* semantics.
*
* The default scope runs with REQUIRED and by default will rollback on any
* exception (checked or runtime).
*
* The scope can control the transaction type, isolation and rollback
* semantics.
*
* 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).
*
* 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).
*
* This will return null if the bean is not an enhanced entity bean.
* {@code
* // use the "hr" database
* EbeanServer hrDatabase = Ebean.getServer("hr");
*
* Person person = hrDatabase.find(Person.class, 10);
* }
*
* @param name the name of the server, can use null for the 'default server'
*/
public static EbeanServer getServer(String name) {
return serverMgr.get(name);
}
/**
* Returns the default EbeanServer.
* Ebean.getServer(null);
* {@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();
* }
*
* }
* 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.getDefaultServer().beginTransaction(scope);
}
/**
* Returns the current transaction or null if there is no current transaction
* in scope.
*/
public static Transaction currentTransaction() {
return serverMgr.getDefaultServer().currentTransaction();
}
/**
* The batch will be flushing automatically but you can use this to explicitly
* flush the batch if you like.
*
*
*/
public static void flush() {
currentTransaction().flush();
}
/**
* 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.getDefaultServer().register(transactionCallback);
}
/**
* Commit the current transaction.
*/
public static void commitTransaction() {
serverMgr.getDefaultServer().commitTransaction();
}
/**
* Rollback the current transaction.
*/
public static void rollbackTransaction() {
serverMgr.getDefaultServer().rollbackTransaction();
}
/**
* If the current transaction has already been committed do nothing otherwise
* rollback the transaction.
* {@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.getDefaultServer().endTransaction();
}
/**
* Mark the current transaction as rollback only.
*/
public static void setRollbackOnly() {
serverMgr.getDefaultServer().currentTransaction().setRollbackOnly();
}
/**
* Return a map of the differences between two objects of the same type.
* {@code
* public class Order { ...
*
* @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
* List
* {@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.getDefaultServer().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().
* {@code
*
* // A 'stateless update' example
* Customer customer = new Customer();
* customer.setId(7);
* customer.setName("ModifiedNameNoOCC");
*
* DB.update(customer);
*
* }
*
* @see ServerConfig#setUpdatesDeleteMissingChildren(boolean)
* @see ServerConfig#setUpdateChangesOnly(boolean)
*/
public static void update(Object bean) throws OptimisticLockException {
serverMgr.getDefaultServer().update(bean);
}
/**
* Update the beans in the collection.
*/
public static void updateAll(Collection> beans) throws OptimisticLockException {
serverMgr.getDefaultServer().updateAll(beans);
}
/**
* Merge the bean using the default merge options.
*
* @param bean The bean to merge
*/
public static void merge(Object bean) {
serverMgr.getDefaultServer().merge(bean);
}
/**
* Merge the bean using the given merge options.
*
* @param bean The bean to merge
* @param options The options to control the merge
*/
public static void merge(Object bean, MergeOptions options) {
serverMgr.getDefaultServer().merge(bean, options);
}
/**
* Save all the beans from a Collection.
*/
public static int saveAll(Collection> beans) throws OptimisticLockException {
return serverMgr.getDefaultServer().saveAll(beans);
}
/**
* This method checks the uniqueness of a bean. I.e. if the save will work. It will return the
* properties that violates an unique / primary key. This may be done in an UI save action to
* validate if the user has entered correct values.
* {@code
*
* // there is a unique constraint on title
*
* Document doc = new Document();
* doc.setTitle("One flew over the cuckoo's nest");
* doc.setBody("clashes with doc1");
*
* Set
*
* @param bean The entity bean to check uniqueness on
* @return a set of Properties if constraint validation was detected or empty list.
*/
@Nonnull
public static Set@SoftDelete then this will perform a soft
* delete rather than a hard/permanent delete.
* {@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.getDefaultServer().refreshMany(bean, manyPropertyName);
}
/**
* Get a reference object.
* {@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
*
* {@code
*
* // find orders and their customers
* List
*
* @param list the list of entity beans
* @param sortByClause the properties to sort the list by
*/
public static {@code
*
* // Fetch order 1
* Order order = Ebean.find(Order.class, 1);
*
* }
* {@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
*
* @param beanType the type of entity bean to fetch
* @param id the id value
*/
@Nullable
public static {@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
*/
public static Example
* {@code
*
* // Find order additionally fetching the customer, details and details.product name.
*
* String eql = "fetch customer fetch details fetch details.product (name) where id = :orderId ";
*
* Query
*
* @param beanType The type of bean to fetch
* @param eql The Ebean query
* @param {@code
*
* String sql = "select c.id, c.name from customer c where c.name like ? order by c.name";
*
* List
*
* @param beanType The type of entity bean to fetch
* @param nativeSql The SQL that can contain named or positioned parameters
* @return The query to set parameters and execute
*/
public static {@code
*
* int rows = Ebean.update(Customer.class)
* .set("status", Customer.Status.ACTIVE)
* .set("updtime", new Timestamp(System.currentTimeMillis()))
* .where()
* .gt("id", 1000)
* .update();
*
* }
*
* @param beanType The type of entity bean to update
* @param {@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.getDefaultServer().execute(sqlUpdate);
}
/**
* For making calls to stored procedures.
* {@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.getDefaultServer().execute(callableSql);
}
/**
* Execute a TxRunnable in a Transaction with an explicit scope.
* {@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, Runnable r) {
serverMgr.getDefaultServer().execute(scope, r);
}
/**
* Execute a Runnable in a Transaction with the default scope.
* {@code
*
* Ebean.execute(() -> {
*
* 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(Runnable r) {
serverMgr.getDefaultServer().execute(r);
}
/**
* Execute a Callable in a Transaction with an explicit scope.
* {@code
*
* // set specific transactional scope settings
* TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
*
* Ebean.executeCall(scope, new Callable
*/
public static {@code
*
* Ebean.executeCall(() -> {
*
* 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