001package io.ebean;
002
003import io.ebean.bean.EntityBean;
004
005
006/**
007 * A MappedSuperclass base class that provides convenience methods for inserting, updating and
008 * deleting beans.
009 * <p>
010 * By having your entity beans extend this it provides a 'Active Record' style programming model for
011 * Ebean users.
012 * <p>
013 * Note that there is a ebean-mocker project that enables you to use Mockito or similar
014 * tools to still mock out the underlying 'default EbeanServer' for testing purposes.
015 * <p>
016 * You may choose not use this Model mapped superclass if you don't like the 'Active Record' style
017 * or if you believe it 'pollutes' your entity beans.
018 * <p>
019 * You can use Dependency Injection like Guice or Spring to construct and wire a EbeanServer instance
020 * and have that same instance used with this Model and Finder. The way that works is that when the
021 * DI container creates the EbeanServer instance it can be registered with the Ebean singleton. In this
022 * way the EbeanServer instance can be injected as per normal Guice / Spring dependency injection and
023 * that same instance also used to support the Model and Finder active record style.
024 * <p>
025 * If you choose to use the Model mapped superclass you will probably also chose to additionally add
026 * a {@link Finder} as a public static field to complete the active record pattern and provide a
027 * relatively nice clean way to write queries.
028 * <p>
029 * <h3>Typical common @MappedSuperclass</h3>
030 * <pre>{@code
031 *
032 *     // Typically there is a common base model that has some
033 *     // common properties like the ones below
034 *
035 *     @MappedSuperclass
036 *     public class BaseModel extends Model {
037 *
038 *       @Id Long id;
039 *
040 *       @Version Long version;
041 *
042 *       @WhenCreated Timestamp whenCreated;
043 *
044 *       @WhenUpdated Timestamp whenUpdated;
045 *
046 *       ...
047 *
048 * }</pre>
049 * <p>
050 * <h3>Extend the Model</h3>
051 * <pre>{@code
052 *
053 *     // Extend the mappedSuperclass
054 *
055 *     @Entity @Table(name="o_account")
056 *     public class Customer extends BaseModel {
057 *
058 *       String name;
059 *       ...
060 *     }
061 *
062 * }</pre>
063 * <p>
064 * <h3>Modal: save()</h3>
065 * <pre>{@code
066 *
067 *     // Active record style ... save(), delete() etc
068 *     Customer customer = new Customer();
069 *     customer.setName("AC234");
070 *
071 *     // save() method inherited from Model
072 *     customer.save();
073 *
074 * }</pre>
075 */
076public abstract class Model {
077
078  /**
079   * Return the underlying 'default' Database.
080   * <p>
081   * This provides full access to the API such as explicit transaction demarcation etc.
082   * <p>
083   * Example:
084   * <pre>{@code
085   *
086   * try (Transaction transaction = Customer.db().beginTransaction()) {
087   *
088   *   // turn off cascade persist for this transaction
089   *   transaction.setPersistCascade(false);
090   *
091   *   // extra control over jdbc batching for this transaction
092   *   transaction.setBatchGetGeneratedKeys(false);
093   *   transaction.setBatchMode(true);
094   *   transaction.setBatchSize(20);
095   *
096   *   Customer customer = new Customer();
097   *   customer.setName(&quot;Roberto&quot;);
098   *   customer.save();
099   *
100   *   Customer otherCustomer = new Customer();
101   *   otherCustomer.setName("Franko");
102   *   otherCustomer.save();
103   *
104   *   transaction.commit();
105   *
106   * }
107   *
108   * }</pre>
109   */
110  public static Database db() {
111    return DB.getDefault();
112  }
113
114  /**
115   * Return a named Database that is typically different to the default database.
116   *
117   * @param server The name of the Database. If this is null then the default Database is returned.
118   */
119  public static Database db(String server) {
120    return DB.byName(server);
121  }
122
123  /**
124   * Marks the entity bean as dirty.
125   * <p>
126   * This is used so that when a bean that is otherwise unmodified is updated the version
127   * property is updated.
128   * <p>
129   * An unmodified bean that is saved or updated is normally skipped and this marks the bean as
130   * dirty so that it is not skipped.
131   * <p>
132   * <pre>{@code
133   *
134   * Customer customer = Customer.find.byId(id);
135   *
136   * // mark the bean as dirty so that a save() or update() will
137   * // increment the version property
138   * customer.markAsDirty();
139   * customer.save();
140   *
141   * }</pre>
142   *
143   * @see Database#markAsDirty(Object)
144   */
145  public void markAsDirty() {
146    db().markAsDirty(this);
147  }
148
149  /**
150   * Mark the property as unset or 'not loaded'.
151   * <p>
152   * This would be used to specify a property that we did not wish to include in a stateless update.
153   * </p>
154   * <pre>{@code
155   *
156   *   // populate an entity bean from JSON or whatever
157   *   User user = ...;
158   *
159   *   // mark the email property as 'unset' so that it is not
160   *   // included in a 'stateless update'
161   *   user.markPropertyUnset("email");
162   *
163   *   user.update();
164   *
165   * }</pre>
166   *
167   * @param propertyName the name of the property on the bean to be marked as 'unset'
168   */
169  public void markPropertyUnset(String propertyName) {
170    ((EntityBean) this)._ebean_getIntercept().setPropertyLoaded(propertyName, false);
171  }
172
173  /**
174   * Insert or update this entity depending on its state.
175   * <p>
176   * Ebean will detect if this is a new bean or a previously fetched bean and perform either an
177   * insert or an update based on that.
178   *
179   * @see Database#save(Object)
180   */
181  public void save() {
182    db().save(this);
183  }
184
185  /**
186   * Flush any batched changes to the database.
187   * <p>
188   * When using JDBC batch flushing occurs automatically at commit() time or when the batch size
189   * is reached. This provides the ability to manually flush the batch.
190   * </p>
191   */
192  public void flush() {
193    db().flush();
194  }
195
196  /**
197   * Update this entity.
198   *
199   * @see Database#update(Object)
200   */
201  public void update() {
202    db().update(this);
203  }
204
205  /**
206   * Insert this entity.
207   *
208   * @see Database#insert(Object)
209   */
210  public void insert() {
211    db().insert(this);
212  }
213
214  /**
215   * Delete this bean.
216   * <p>
217   * This will return true if the bean was deleted successfully or JDBC batch is being used.
218   * </p>
219   * <p>
220   * If there is no current transaction one will be created and committed for
221   * you automatically.
222   * </p>
223   * <p>
224   * If the Bean does not have a version property (or loaded version property) and
225   * the bean does not exist then this returns false indicating that nothing was
226   * deleted. Note that, if JDBC batch mode is used then this always returns true.
227   * </p>
228   *
229   * @see Database#delete(Object)
230   */
231  public boolean delete() {
232    return db().delete(this);
233  }
234
235  /**
236   * Delete a bean permanently without soft delete.
237   * <p>
238   * This is used when the bean contains a <code>@SoftDelete</code> property and we
239   * want to perform a hard/permanent delete.
240   * </p>
241   *
242   * @see Database#deletePermanent(Object)
243   */
244  public boolean deletePermanent() {
245    return db().deletePermanent(this);
246  }
247
248  /**
249   * Perform an update using this entity against the specified server.
250   */
251  public void update(String server) {
252    db(server).update(this);
253  }
254
255  /**
256   * Perform an insert using this entity against the specified server.
257   */
258  public void insert(String server) {
259    db(server).insert(this);
260  }
261
262  /**
263   * Perform a delete using this entity against the specified server.
264   */
265  public boolean delete(String server) {
266    return db(server).delete(this);
267  }
268
269  /**
270   * Refreshes this entity from the database.
271   *
272   * @see Database#refresh(Object)
273   */
274  public void refresh() {
275    db().refresh(this);
276  }
277
278}