![JAR search and dependency download from the Maven repository](/logo.png)
com.avaje.ebean.Model Maven / Gradle / Ivy
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);
}
}
/**
* 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 equivalent 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 a find row count query in a background thread.
*
* Equivalent to {@link Query#findFutureCount()}
*/
public FutureRowCount findFutureCount() {
return query().findFutureCount();
}
/**
* Deprecated in favor of findFutureCount().
*
* Equivalent to {@link Query#findFutureCount()}
*/
public FutureRowCount findFutureRowCount() {
return query().findFutureCount();
}
/**
* Returns the total number of entities for this type. *
*
* Equivalent to {@link Query#findCount()}
*/
public int findCount() {
return query().findCount();
}
/**
* Deprecated in favor of findCount().
* @deprecated
*/
public int findRowCount() {
return query().findCount();
}
/**
* 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 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);
}
}
}