All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.avaje.ebean.Model Maven / Gradle / Ivy

There is a newer version: 8.1.1
Show newest version
package com.avaje.ebean;

import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.util.ClassUtil;
import org.jetbrains.annotations.Nullable;

import javax.persistence.MappedSuperclass;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
 * A MappedSuperclass base class that provides convenience methods for inserting, updating and
 * deleting beans.
 * 
 * 

* By having your entity beans extend this it provides a 'Active Record' style programming model for * Ebean users. * *

* Note that there is a avaje-ebeanorm-mocker project that enables you to use Mockito or similar * tools to still mock out the underlying 'default EbeanServer' for testing purposes. * *

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

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

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

Typical common @MappedSuperclass

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

Extend the Model

*
{@code
 *
 *     // Extend the mappedSuperclass
 *
 *     @Entity @Table(name="oto_account")
 *     public class Customer extends BaseModel {
 *
 *       // Add a static Find
 *       // ... with Long being the type of our @Id property.
 *       // ... Note the {} at the end as Find is an abstract class.
 *
 *       public static final Find find = new Find(){};
 *
 *       String name;
 *       ...
 *     }
 *
 * }
* *

Modal: save()

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

Find byId

*
{@code
 *
 *     // find byId
 *     Customer customer = Customer.find.byId(42);
 *
 * }
* *

Find where

*
{@code
 *
 *     // find where ...
 *     List customers =
 *         Customer.find
 *         .where().gt("startDate", lastMonth)
 *         .findList();
 *
 * }
*/ @MappedSuperclass public abstract class Model { /** * Return the underlying 'default' EbeanServer. * *

* This provides full access to the API such as explicit transaction demarcation etc. * *

* Example: *

{@code
   *
   * Transaction transaction = Customer.db().beginTransaction();
   * try {
   * 
   *   // turn off cascade persist for this transaction
   *   transaction.setPersistCascade(false);
   * 
   *   // extra control over jdbc batching for this transaction
   *   transaction.setBatchGetGeneratedKeys(false);
   *   transaction.setBatchMode(true);
   *   transaction.setBatchSize(20);
   * 
   *   Customer customer = new Customer();
   *   customer.setName("Roberto");
   *   customer.save();
   * 
   *   Customer otherCustomer = new Customer();
   *   otherCustomer.setName("Franko");
   *   otherCustomer.save();
   * 
   *   transaction.commit();
   * 
   * } finally {
   *   transaction.end();
   * }
   * 
   * }
*/ public static EbeanServer db() { return Ebean.getDefaultServer(); } /** * Return a named EbeanServer that is typically different to the default server. * *

* If you are using multiple databases then each database has a name and maps to a single * EbeanServer. You can use this method to get an EbeanServer for another database. * * @param server * The name of the EbeanServer. If this is null then the default EbeanServer is returned. */ public static EbeanServer db(String server) { return Ebean.getServer(server); } /** * Marks the entity bean as dirty. *

* This is used so that when a bean that is otherwise unmodified is updated the version * property is 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 = Customer.find.byId(id);
   * 
   * // mark the bean as dirty so that a save() or update() will
   * // increment the version property
   * customer.markAsDirty();
   * customer.save();
   * 
   * }
* * @see EbeanServer#markAsDirty(Object) */ public void markAsDirty() { db().markAsDirty(this); } /** * Mark the property as unset or 'not loaded'. *

* This would be used to specify a property that we did not wish to include in a stateless update. *

*
{@code
   *
   *   // populate an entity bean from JSON or whatever
   *   User user = ...;
   *
   *   // mark the email property as 'unset' so that it is not
   *   // included in a 'stateless update'
   *   user.markPropertyUnset("email");
   *
   *   user.update();
   *
   * }
* * @param propertyName the name of the property on the bean to be marked as 'unset' */ public void markPropertyUnset(String propertyName) { ((EntityBean)this)._ebean_getIntercept().setPropertyLoaded(propertyName, false); } /** * Insert or update this entity depending on its state. * *

* Ebean will detect if this is a new bean or a previously fetched bean and perform either an * insert or an update based on that. * * @see EbeanServer#save(Object) */ public void save() { db().save(this); } /** * Update this entity. * * @see EbeanServer#update(Object) */ public void update() { db().update(this); } /** * Insert this entity. * * @see EbeanServer#insert(Object) */ public void insert() { db().insert(this); } /** * Delete this 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 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. *

* * @see EbeanServer#delete(Object) */ public boolean delete() { return db().delete(this); } /** * Delete a bean permanently without soft delete. *

* This is used when the bean contains a @SoftDelete property and we * want to perform a hard/permanent delete. *

* * @see EbeanServer#deletePermanent(Object) */ public boolean deletePermanent() { return db().deletePermanent(this); } /** * Perform an update using this entity against the specified server. */ public void update(String server) { db(server).update(this); } /** * Perform an insert using this entity against the specified server. */ public void insert(String server) { db(server).insert(this); } /** * Perform a delete using this entity against the specified server. */ public boolean delete(String server) { return db(server).delete(this); } /** * Refreshes this entity from the database. * * @see EbeanServer#refresh(Object) */ public void refresh() { db().refresh(this); } /** * A concrete implementation of Find. *

* It should be preferred to use {@link Find} instead of Finder as that can use reflection to determine the class * literal type of the entity bean. *

* @param type of the Id property * @param type of the entity bean */ public static class Finder extends Find { /** * Create with the type of the entity bean. * *
{@code
     *
     * @Entity
     * public class Customer extends BaseModel {
     *
     *   public static final Finder find = new Finder(Customer.class);
     *   ...
     *
     * }
* *

* The preferred approach is to instead use Find as below. This approach is more DRY in that it does * not require the class literal Customer.class to be passed into the constructor. * *

{@code
     *
     * @Entity
     * public class Customer extends BaseModel {
     *
     *   public static final Find find = new Find(){};
     *   ...
     *
     * }
*/ public Finder(Class type) { super(null, type); } /** * Create with the type of the entity bean and specific server name. */ public Finder(String serverName, Class type) { super(serverName, type); } /** * Please migrate to use {@link Find} or constructor Finder(Class) that * does not have the idType parameter. *

* Create with the type of the ID property and entity bean and specific server name. * * @deprecated */ public Finder(Class idType, Class type) { super(null, type); } /** * Please migrate to use the constructor Finder(String, Class) that * does not have the idType parameter. *

* Create with the type of the ID property and entity bean and specific server name. * * @deprecated */ public Finder(String serverName, Class idType, Class type) { super(serverName, type); } } /** * Helper object for performing queries. * *

* Typically a Find instance is defined as a public static field on an entity bean class to provide a * nice way to write queries. * *

Example use:

* *
{@code
   *
   * @Entity
   * public class Customer extends BaseModel {
   *
   *   public static final Find find = new Find(){};
   *
   *   ...
   *
   * }
*

* This enables you to write code like: *

{@code
   *
   * Customer customer = Customer.find.byId(42L);
   *
   * List customers =
   *     Customer.find
   *         .select("name, dateOfBirth")
   *         .findList();
   *
   * }
* *

Kotlin

* In Kotlin you would typically create Find as a companion object. *
{@code
   *
   *   // kotlin
   *   companion object : Model.Find() {}
   *
   * }
* @param * The Id type. This is most often a {@link Long} but is also often a {@link UUID} or * {@link String}. * * @param * The entity bean type */ public static abstract class Find { /** * The entity bean type. */ private final Class type; /** * The name of the EbeanServer, null for the default server. */ private final String serverName; /** * Creates a finder for entity of type T with ID of type I. *

* Typically you create Find as a public static field on each entity bean as the example below. * *

* Note that Find is an abstract class and hence {} is required. This is done so * that the type (class literal) of the entity bean can be derived from the generics parameter. * *

{@code
     *
     * @Entity
     * public class Customer extends BaseModel {
     *
     *   // Note the trailing {} as Find is an abstract class.
     *   // We do this so that we can derive the type literal Customer.class
     *   // via reflection
     *   public static final Find find = new Find(){};
     *   ...
     *
     * }
*

* This enables you to write code like: *

{@code
     *
     * Customer customer = Customer.find.byId(42L);
     *
     * List customers =
     *     Customer.find
     *        .select("name, email, dateOfBirth")
     *        .findList();
     *
     * }
* *

Kotlin

* In Kotlin you would typically create it as a companion object. * *
{@code
     *
     *   // kotlin
     *   companion object : Model.Find() {}
     *
     * }
*/ @SuppressWarnings("unchecked") public Find() { this.serverName = null; this.type = (Class)ClassUtil.getSecondArgumentType(getClass()); } /** * Construct passing the class literal type of the entity type. */ protected Find(String serverName, Class type) { this.serverName = serverName; this.type = type; } /** * Return the underlying 'default' EbeanServer. * *

* This provides full access to the API such as explicit transaction demarcation etc. * */ public EbeanServer db() { return Ebean.getServer(serverName); } /** * Return typically a different EbeanServer to the default. *

* This is equivilent to {@link Ebean#getServer(String)} * * @param server * The name of the EbeanServer. If this is null then the default EbeanServer is * returned. */ public EbeanServer db(String server) { return Ebean.getServer(server); } /** * Creates a Finder for the named EbeanServer. * *

* Create and return a new Finder for a different server. */ public Finder on(String server) { return new Finder(server, type); } /** * Delete a bean by Id. *

* Equivalent to {@link EbeanServer#delete(Class, Object)} */ public void deleteById(I id) { db().delete(type, id); } /** * Retrieves all entities of the given type. * *

* This is the same as (synonym for) {@link #findList()} */ public List all() { return findList(); } /** * Retrieves an entity by ID. * *

* Equivalent to {@link EbeanServer#find(Class, Object)} */ @Nullable public T byId(I id) { return db().find(type, id); } /** * Creates an entity reference for this ID. * *

* Equivalent to {@link EbeanServer#getReference(Class, Object)} */ public T ref(I id) { return db().getReference(type, id); } /** * Creates a filter for sorting and filtering lists of entities locally without going back to * the database. *

* Equivalent to {@link EbeanServer#filter(Class)} */ public Filter filter() { return db().filter(type); } /** * Creates a query. *

* Equivalent to {@link EbeanServer#find(Class)} */ public Query query() { return db().find(type); } /** * Creates a query applying the path properties to set the select and fetch clauses. *

* Equivalent to {@link Query#apply(FetchPath)} */ public Query apply(FetchPath fetchPath) { return db().find(type).apply(fetchPath); } /** * Returns the next identity value. * * @see EbeanServer#nextId(Class) */ @SuppressWarnings("unchecked") public I nextId() { return (I) db().nextId(type); } /** * Executes a query and returns the results as a list of IDs. *

* Equivalent to {@link Query#findIds()} */ public List findIds() { return query().findIds(); } /** * Execute the query consuming each bean one at a time. *

* This is generally used to process large queries where unlike findList * you do not want to hold all the results in memory at once but instead * process them one at a time (requiring far less memory). *

* Equivalent to {@link Query#findEach(QueryEachConsumer)} */ public void findEach(QueryEachConsumer consumer) { query().findEach(consumer); } /** * Execute the query consuming each bean one at a time. *

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

* This is similar to #findEach except that you return boolean * true to continue processing beans and return false to stop * processing early. *

*

* This is generally used to process large queries where unlike findList * you do not want to hold all the results in memory at once but instead * process them one at a time (requiring far less memory). *

* Equivalent to {@link Query#findEachWhile(QueryEachWhileConsumer)} */ public void findEachWhile(QueryEachWhileConsumer consumer) { query().findEachWhile(consumer); } /** * Retrieves all entities of the given type. *

* The same as {@link #all()} *

* Equivalent to {@link Query#findList()} */ public List findList() { return query().findList(); } /** * Returns all the entities of the given type as a set. *

* Equivalent to {@link Query#findSet()} */ public Set findSet() { return query().findSet(); } /** * Retrieves all entities of the given type as a map of objects. *

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

* Equivalent to {@link Query#findMap(String, Class)} */ public Map findMap(String keyProperty, Class keyType) { return query().findMap(keyProperty, keyType); } /** * Return a PagedList of all entities of the given type (use where() to specify predicates as * needed). *

* Equivalent to {@link Query#findPagedList(int, int)} */ public PagedList findPagedList(int pageIndex, int pageSize) { return query().findPagedList(pageIndex, pageSize); } /** * Executes a find row count query in a background thread. *

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

* Equivalent to {@link Query#findRowCount()} */ public int findRowCount() { return query().findRowCount(); } /** * Returns the ExpressionFactory used by this query. */ public ExpressionFactory getExpressionFactory() { return query().getExpressionFactory(); } /** * Explicitly sets a comma delimited list of the properties to fetch on the 'main' entity bean, * to load a partial object. *

* Equivalent to {@link Query#select(String)} */ public Query select(String fetchProperties) { return query().select(fetchProperties); } /** * Specifies a path to load including all its properties. *

* Equivalent to {@link Query#fetch(String)} */ public Query fetch(String path) { return query().fetch(path); } /** * Additionally specifies a FetchConfig to specify a 'query join' and/or define the * lazy loading query. *

* Equivalent to {@link Query#fetch(String, FetchConfig)} */ public Query fetch(String path, FetchConfig joinConfig) { return query().fetch(path, joinConfig); } /** * Specifies a path to fetch with a specific list properties to include, to load a partial * object. *

* Equivalent to {@link Query#fetch(String, String)} */ public Query fetch(String path, String fetchProperties) { return query().fetch(path, fetchProperties); } /** * Additionally specifies a FetchConfig to use a separate query or lazy loading to * load this path. *

* Equivalent to {@link Query#fetch(String, String, FetchConfig)} */ public Query fetch(String assocProperty, String fetchProperties, FetchConfig fetchConfig) { return query().fetch(assocProperty, fetchProperties, fetchConfig); } /** * Adds expressions to the where clause with the ability to chain on the * ExpressionList. *

* Equivalent to {@link Query#where()} */ public ExpressionList where() { return query().where(); } /** * Returns the order by clause so that you can append an ascending or descending * property to the order by clause. *

* This is exactly the same as {@link #orderBy}. *

* Equivalent to {@link Query#order()} */ public OrderBy order() { return query().order(); } /** * Sets the order by clause, replacing the existing order by clause if * there is one. *

* This is exactly the same as {@link #orderBy(String)}. */ public Query order(String orderByClause) { return query().order(orderByClause); } /** * Returns the order by clause so that you can append an ascending or descending * property to the order by clause. *

* This is exactly the same as {@link #order}. *

* Equivalent to {@link Query#orderBy()} */ public OrderBy orderBy() { return query().orderBy(); } /** * Set the order by clause replacing the existing order by clause if * there is one. *

* This is exactly the same as {@link #order(String)}. */ public Query orderBy(String orderByClause) { return query().orderBy(orderByClause); } /** * Sets the first row to return for this query. *

* Equivalent to {@link Query#setFirstRow(int)} */ public Query setFirstRow(int firstRow) { return query().setFirstRow(firstRow); } /** * Sets the maximum number of rows to return in the query. *

* Equivalent to {@link Query#setMaxRows(int)} */ public Query setMaxRows(int maxRows) { return query().setMaxRows(maxRows); } /** * Sets the ID value to query. * *

* Use this to perform a find byId query but with additional control over the query such as * using select and fetch to control what parts of the object graph are returned. *

* Equivalent to {@link Query#setId(Object)} */ public Query setId(Object id) { return query().setId(id); } /** * Create and return a new query using the OQL. *

* Equivalent to {@link EbeanServer#createQuery(Class, String)} */ public Query setQuery(String oql) { return db().createQuery(type, oql); } /** * Create and return a new query based on the RawSql. *

* Equivalent to {@link Query#setRawSql(RawSql)} */ public Query setRawSql(RawSql rawSql) { return query().setRawSql(rawSql); } /** * Create a query with explicit 'AutoTune' use. */ public Query setAutoTune(boolean autoTune) { return query().setAutoTune(autoTune); } /** * Create a query with the select with "for update" specified. * *

* This will typically create row level database locks on the selected rows. */ public Query setForUpdate(boolean forUpdate) { return query().setForUpdate(forUpdate); } /** * Create a query specifying whether the returned beans will be read-only. */ public Query setReadOnly(boolean readOnly) { return query().setReadOnly(readOnly); } /** * Create a query specifying if the beans should be loaded into the L2 cache. */ public Query setLoadBeanCache(boolean loadBeanCache) { return query().setLoadBeanCache(loadBeanCache); } /** * Create a query specifying if the L2 bean cache should be used. */ public Query setUseCache(boolean useBeanCache) { return query().setUseCache(useBeanCache); } /** * Create a query specifying if the L2 query cache should be used. */ public Query setUseQueryCache(boolean useQueryCache) { return query().setUseQueryCache(useQueryCache); } } }