diff --git a/src/main/java/com/avaje/ebean/Model.java b/src/main/java/com/avaje/ebean/Model.java new file mode 100644 index 000000000..ec66a0c7e --- /dev/null +++ b/src/main/java/com/avaje/ebean/Model.java @@ -0,0 +1,774 @@ +package com.avaje.ebean; + +import java.util.*; +import java.beans.*; +import java.lang.reflect.*; + +import com.avaje.ebean.Ebean; +import com.avaje.ebean.OrderBy; +import com.avaje.ebean.ExpressionList; +import com.avaje.ebean.Query; +import com.avaje.ebean.RawSql; +import com.avaje.ebean.ExpressionFactory; +import com.avaje.ebean.PagingList; +import com.avaje.ebean.FutureRowCount; +import com.avaje.ebean.FutureList; +import com.avaje.ebean.FutureIds; +import com.avaje.ebean.FetchConfig; +//import com.avaje.ebean.QueryListener; +//import com.avaje.ebean.QueryIterator; +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.Filter; +//import com.avaje.ebean.QueryResultVisitor; + +//import play.Play; +//import play.libs.F.*; +//import static play.libs.F.*; + +//import org.springframework.beans.*; + +/** + * Base-class for Ebean-mapped models that provides convenience methods. + */ +@javax.persistence.MappedSuperclass +public class Model { + + // -- Magic to dynamically access the @Id property + + // @javax.persistence.Transient + // private Tuple _idGetSet; + // + // private Tuple _idAccessors() { + // if(_idGetSet == null) { + // try { + // Class clazz = this.getClass(); + // while(clazz != null) { + // for(Field f:clazz.getDeclaredFields()) { + // if(f.isAnnotationPresent(javax.persistence.Id.class) || + // f.isAnnotationPresent(javax.persistence.EmbeddedId.class)) { + // PropertyDescriptor idProperty = new BeanWrapperImpl(this).getPropertyDescriptor(f.getName()); + // _idGetSet = Tuple(idProperty.getReadMethod() , idProperty.getWriteMethod()); + // } + // } + // clazz = clazz.getSuperclass(); + // } + // if(_idGetSet == null) { + // throw new RuntimeException("No @javax.persistence.Id field found in class [" + this.getClass() + // + "]"); + // } + // } catch(RuntimeException e) { + // throw e; + // } catch(Exception e) { + // throw new RuntimeException(e); + // } + // } + // return _idGetSet; + // } + + private Object _getId() { + try { + return null;// _idAccessors()._1.invoke(this); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void _setId(Object id) { + try { + // _idAccessors()._2.invoke(this,id); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + // -- + + /** + * Save inserts or updates this entity depending on its state. + */ + public void save() { + Ebean.save(this); + } + + /** + * Return the default EbeanServer. + */ + public EbeanServer db() { + return Ebean.getServer(null); + } + + /** + * Return typically a different EbeanServer to the default. + * @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); + } + + // RB: Actually they should probably not use save() but insert() in this case +// /** +// * Saves (inserts) this entity. +// * +// * @param server +// * the Ebean server to use +// */ +// public void save(String server) { +// Ebean.getServer(server).save(this); +// } + + // RB: Uncommonly used so get this method off the db() so ... db().saveManyToManyAssociations(this, path) +// +// /** +// * Persist a many-to-many association. +// */ +// public void saveManyToManyAssociations(String path) { +// Ebean.saveManyToManyAssociations(this, path); +// } + + // RB: Uncommonly used so get this method off the db() +// /** +// * Persist a many-to-many association. +// * +// * @param server +// * the Ebean server to use +// */ +// public void saveManyToManyAssociations(String server, String path) { +// Ebean.getServer(server).saveManyToManyAssociations(this, path); +// } + + // RB: Uncommonly used so get this method off the db() +// /** +// * Deletes a many-to-many association +// * +// * @param path +// * name of the many-to-many association we want to delete +// */ +// public void deleteManyToManyAssociations(String path) { +// Ebean.deleteManyToManyAssociations(this, path); +// } + + /** + * Updates this entity. + */ + public void update() { + Ebean.update(this); + } + + // RB: Uncommon - just use db(server).update(this); +// /** +// * Updates this entity, using a specific Ebean server. +// * +// * @param server +// * the Ebean server to use +// */ +// public void update(String server) { +// Ebean.getServer(server).update(this); +// } + + // RB: ?? +// /** +// * Updates this entity, by specifying the entity ID. +// */ +// public void update(Object id) { +// _setId(id); +// Ebean.update(this); +// } + + // RB: Again ?? +// /** +// * Updates this entity, by specifying the entity ID, using a specific Ebean server. +// * +// * @param server +// * the Ebean server to use +// */ +// public void update(Object id, String server) { +// _setId(id); +// Ebean.getServer(server).update(this); +// } + + /** + * Deletes this entity. + */ + public void delete() { + Ebean.delete(this); + } + + // RB: Uncommon - always use db(server) to get other EbeanServer instances +// /** +// * Deletes this entity, using a specific Ebean server. +// * +// * @param server +// * the Ebean server to use +// */ +// public void delete(String server) { +// Ebean.getServer(server).delete(this); +// } + + /** + * Refreshes this entity from the database. + */ + public void refresh() { + Ebean.refresh(this); + } + +// /** +// * Refreshes this entity from the database, using a specific Ebean server. +// * +// * @param server +// * the Ebean server to use +// */ +// public void refresh(String server) { +// Ebean.getServer(server).refresh(this); +// } + + @Override + public boolean equals(Object other) { + if (this == other) + return true; + if (other == null || other.getClass() != this.getClass()) + return false; + Object id = _getId(); + Object otherId = ((Model) other)._getId(); + if (id == null) + return false; + if (otherId == null) + return false; + return id.equals(otherId); + } + + @Override + public int hashCode() { + Object id = _getId(); + return id == null ? super.hashCode() : id.hashCode(); + } + + /** + * Helper for Ebean queries. + */ + public static class Finder {// implements Query { + + private final Class idType; + private final Class type; + private final String serverName; + + /** + * Creates a finder for entity of type T with ID of type I. + */ + public Finder(Class idType, Class type) { + this(null, idType, type); + } + + /** + * Creates a finder for entity of type T with ID of type I, using a + * specific Ebean server. + */ + public Finder(String serverName, Class idType, Class type) { + this.type = type; + this.idType = idType; + this.serverName = serverName; + } + + private EbeanServer server() { + return Ebean.getServer(serverName); + } + + /** + * Changes the Ebean server. + */ + public Finder on(String server) { + return new Finder(server, idType, type); + } + + /** + * Retrieves all entities of the given type. + */ + public List all() { + return server().find(type).findList(); + } + + /** + * Retrieves an entity by ID. + */ + public T byId(I id) { + return server().find(type, id); + } + + /** + * Retrieves an entity reference for this ID. + * + * @deprecated RB: Move to Model + */ + public T ref(I id) { + return server().getReference(type, id); + } + + /** + * Creates a filter for sorting and filtering lists of entities locally without going back to + * the database. + */ + public Filter filter() { + return server().filter(type); + } + + /** + * Creates a query. + */ + public Query query() { + return server().find(type); + } + + /** + * Returns the next identity value. + * + * @deprecated RB: move to model + */ + public I nextId() { + return (I) server().nextId(type); + } + + // /** + // * Cancels query execution, if supported by the underlying database and driver. + // */ + // public void cancel() { + // query().cancel(); + // } + + // /** + // * Copies this query. + // */ + // public Query copy() { + // return query().copy(); + // } + + /** + * Specifies a path to load including all its properties. + */ + 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. + */ + 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. + */ + 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. + */ + public Query fetch(String assocProperty, String fetchProperties, FetchConfig fetchConfig) { + return query().fetch(assocProperty, fetchProperties, fetchConfig); + } + +// /** +// * Applies a filter on the 'many' property list rather than the root level objects. +// * +// * @deprecated RB: Get this off the query(). Advanced feature. +// */ +// public ExpressionList filterMany(String propertyName) { +// return query().filterMany(propertyName); +// } + + /** + * Executes a find IDs query in a background thread. + * + * @deprecated RB: Hmm, this is a "find all" - less is more, hide from newbies. + */ + public FutureIds findFutureIds() { + return query().findFutureIds(); + } + + /** + * Executes a find list query in a background thread. + * + * @deprecated RB:underlying method deprecated + */ + public FutureList findFutureList() { + return query().findFutureList(); + } + + /** + * Executes a find row count query in a background thread. + */ + public FutureRowCount findFutureRowCount() { + return query().findFutureRowCount(); + } + + /** + * Executes a query and returns the results as a list of IDs. + */ + public List findIds() { + return query().findIds(); + } + + /** + * Executes the query and returns the results as a list of objects. + */ + public List findList() { + return query().findList(); + } + + /** + * Executes the query and returns the results as a map of objects. + */ + public Map findMap() { + return query().findMap(); + } + + /** + * Executes the query and returns the results as a map of the objects. + */ + public Map findMap(String a, Class b) { + return query().findMap(a, b); + } + + /** + * Returns a PagingList for this query. + * + * @deprecated RB: PagingList deprecated - migrate to findPagedList(). + */ + public PagingList findPagingList(int pageSize) { + return query().findPagingList(pageSize); + } + + /** + * Returns the number of entities this query should return. + * + * + */ + public int findRowCount() { + return query().findRowCount(); + } + + /** + * Executes the query and returns the results as a set of objects. + */ + public Set findSet() { + return query().findSet(); + } + + // /** + // * Executes the query and returns the results as either a single bean or null, if + // no matching bean is found. + // */ + // public T findUnique() { + // return query().findUnique(); + // } + + // public void findVisit(QueryResultVisitor visitor) { + // query().findVisit(visitor); + // } + // + // public QueryIterator findIterate() { + // return query().findIterate(); + // } + + /** + * Returns the ExpressionFactory used by this query. + */ + public ExpressionFactory getExpressionFactory() { + return query().getExpressionFactory(); + } + + // /** + // * Returns the first row value. + // */ + // public int getFirstRow() { + // return query().getFirstRow(); + // } + + // /** + // * Returns the SQL that was generated for executing this query. + // */ + // public String getGeneratedSql() { + // return query().getGeneratedSql(); + // } + + // /** + // * Returns the maximum of rows for this query. + // */ + // public int getMaxRows() { + // return query().getMaxRows(); + // } + + // /** + // * Returns the RawSql that was set to use for this query. + // */ + // public RawSql getRawSql() { + // return query().getRawSql(); + // } + + // /** + // * Returns the query's having clause. + // */ + // public ExpressionList having() { + // return query().having(); + // } + // + // /** + // * Adds an expression to the having clause and returns the query. + // */ + // public Query having(com.avaje.ebean.Expression addExpressionToHaving) { + // return query().having(addExpressionToHaving); + // } + // + // /** + // * Adds clauses to the having clause and returns the query. + // */ + // public Query having(String addToHavingClause) { + // return query().having(addToHavingClause); + // } + // + // /** + // * Returns true if this query was tuned by autoFetch. + // */ + // public boolean isAutofetchTuned() { + // return query().isAutofetchTuned(); + // } + + /** + * 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}. + */ + 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}. + */ + 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); + } + + /** + * Explicitly sets a comma delimited list of the properties to fetch on the 'main' entity bean, + * to load a partial object. + */ + public Query select(String fetchProperties) { + return query().select(fetchProperties); + } + + /** + * Explicitly specifies whether to use 'Autofetch' for this query. + */ + public Query setAutofetch(boolean autofetch) { + return query().setAutofetch(autofetch); + } + + // /** + // * Sets the rows after which fetching should continue in a background thread. + // */ + // public Query setBackgroundFetchAfter(int backgroundFetchAfter) { + // return query().setBackgroundFetchAfter(backgroundFetchAfter); + // } + // + // /** + // * Sets a hint, which for JDBC translates to Statement.fetchSize(). + // */ + // public Query setBufferFetchSizeHint(int fetchSize) { + // return query().setBufferFetchSizeHint(fetchSize); + // } + // + // /** + // * Sets whether this query uses DISTINCT. + // */ + // public Query setDistinct(boolean isDistinct) { + // return query().setDistinct(isDistinct); + // } + + /** + * Sets the first row to return for this query. + */ + public Query setFirstRow(int firstRow) { + return query().setFirstRow(firstRow); + } + + /** + * Sets the ID value to query. + */ + public Query setId(Object id) { + return query().setId(id); + } + + // /** + // * Sets a listener to process the query on a row-by-row basis. + // */ + // public Query setListener(QueryListener queryListener) { + // return query().setListener(queryListener); + // } + + /** + * When set to true, all the beans from this query are loaded into the bean cache. + */ + public Query setLoadBeanCache(boolean loadBeanCache) { + return query().setLoadBeanCache(loadBeanCache); + } + + /** + * Sets the property to use as keys for a map. + */ + public Query setMapKey(String mapKey) { + return query().setMapKey(mapKey); + } + + /** + * Sets the maximum number of rows to return in the query. + */ + public Query setMaxRows(int maxRows) { + return query().setMaxRows(maxRows); + } + + // /** + // * Replaces any existing order by clause using an OrderBy object. + // *

+ // * This is exactly the same as {@link #setOrderBy(com.avaje.ebean.OrderBy)}. + // */ + // public Query setOrder(OrderBy orderBy) { + // return query().setOrder(orderBy); + // } + // + // /** + // * Set an OrderBy object to replace any existing order by clause. + // *

+ // * This is exactly the same as {@link #setOrder(com.avaje.ebean.OrderBy)}. + // */ + // public Query setOrderBy(OrderBy orderBy) { + // return query().setOrderBy(orderBy); + // } + // + // /** + // * Sets an ordered bind parameter according to its position. + // */ + // public Query setParameter(int position, Object value) { + // return query().setParameter(position, value); + // } + // + // /** + // * Sets a named bind parameter. + // */ + // public Query setParameter(String name, Object value) { + // return query().setParameter(name, value); + // } + + /** + * Sets the OQL query to run + */ + public Query setQuery(String oql) { + return server().createQuery(type, oql); + } + + /** + * Sets RawSql to use for this query. + */ + public Query setRawSql(RawSql rawSql) { + return query().setRawSql(rawSql); + } + + /** + * Sets whether the returned beans will be read-only. + */ + public Query setReadOnly(boolean readOnly) { + return query().setReadOnly(readOnly); + } + + // /** + // * Sets a timeout on this query. + // */ + // public Query setTimeout(int secs) { + // return query().setTimeout(secs); + // } + + /** + * Sets whether to use the bean cache. + */ + public Query setUseCache(boolean useBeanCache) { + return query().setUseCache(useBeanCache); + } + + /** + * Sets whether to use the query cache. + */ + public Query setUseQueryCache(boolean useQueryCache) { + return query().setUseQueryCache(useQueryCache); + } + + /** + * Adds expressions to the where clause with the ability to chain on the + * ExpressionList. + */ + public ExpressionList where() { + return query().where(); + } + + /** + * Adds a single Expression to the where clause and returns the query. + */ + public Query where(com.avaje.ebean.Expression expression) { + return query().where(expression); + } + + /** + * Adds additional clauses to the where clause. + */ + public Query where(String addToWhereClause) { + return query().where(addToWhereClause); + } + + /** + * Execute the select with "for update" which should lock the record "on read" + */ + // @Override + public Query setForUpdate(boolean forUpdate) { + return query().setForUpdate(forUpdate); + } + + // /** + // * Whether this query is for update + // */ + // @Override + // public boolean isForUpdate() { + // return query().isForUpdate(); + // } + } +} \ No newline at end of file