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 Database' 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 Database 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 Database instance it can be registered with DB. In this
022 * way the Database 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   * The name of the database this entity will use, null for the default database.
080   */
081  private final String _$dbName;
082
083  /**
084   * Create using the default database.
085   */
086  public Model() {
087    this._$dbName = null;
088  }
089
090  /**
091   * Create with a named database (typically not the default database).
092   */
093  public Model(String dbName) {
094    this._$dbName = dbName;
095  }
096
097  /**
098   * Return the underlying 'default' Database.
099   * <p>
100   * This provides full access to the API such as explicit transaction demarcation etc.
101   * <p>
102   * Example:
103   * <pre>{@code
104   *
105   * try (Transaction transaction = Customer.db().beginTransaction()) {
106   *
107   *   // turn off cascade persist for this transaction
108   *   transaction.setPersistCascade(false);
109   *
110   *   // extra control over jdbc batching for this transaction
111   *   transaction.setBatchGetGeneratedKeys(false);
112   *   transaction.setBatchMode(true);
113   *   transaction.setBatchSize(20);
114   *
115   *   Customer customer = new Customer();
116   *   customer.setName(&quot;Roberto&quot;);
117   *   customer.save();
118   *
119   *   Customer otherCustomer = new Customer();
120   *   otherCustomer.setName("Franko");
121   *   otherCustomer.save();
122   *
123   *   transaction.commit();
124   *
125   * }
126   *
127   * }</pre>
128   */
129  public Database db() {
130    return DB.byName(_$dbName);
131  }
132
133  /**
134   * Marks the entity bean as dirty.
135   * <p>
136   * This is used so that when a bean that is otherwise unmodified is updated the version
137   * property is updated.
138   * <p>
139   * An unmodified bean that is saved or updated is normally skipped and this marks the bean as
140   * dirty so that it is not skipped.
141   * <p>
142   * <pre>{@code
143   *
144   * Customer customer = Customer.find.byId(id);
145   *
146   * // mark the bean as dirty so that a save() or update() will
147   * // increment the version property
148   * customer.markAsDirty();
149   * customer.save();
150   *
151   * }</pre>
152   *
153   * @see Database#markAsDirty(Object)
154   */
155  public void markAsDirty() {
156    db().markAsDirty(this);
157  }
158
159  /**
160   * Mark the property as unset or 'not loaded'.
161   * <p>
162   * This would be used to specify a property that we did not wish to include in a stateless update.
163   * </p>
164   * <pre>{@code
165   *
166   *   // populate an entity bean from JSON or whatever
167   *   User user = ...;
168   *
169   *   // mark the email property as 'unset' so that it is not
170   *   // included in a 'stateless update'
171   *   user.markPropertyUnset("email");
172   *
173   *   user.update();
174   *
175   * }</pre>
176   *
177   * @param propertyName the name of the property on the bean to be marked as 'unset'
178   */
179  public void markPropertyUnset(String propertyName) {
180    ((EntityBean) this)._ebean_getIntercept().setPropertyLoaded(propertyName, false);
181  }
182
183  /**
184   * Insert or update this entity depending on its state.
185   * <p>
186   * Ebean will detect if this is a new bean or a previously fetched bean and perform either an
187   * insert or an update based on that.
188   *
189   * @see Database#save(Object)
190   */
191  public void save() {
192    db().save(this);
193  }
194
195  /**
196   * Save this entity with an explicit transaction.
197   */
198  public void save(Transaction transaction) {
199    db().save(this, transaction);
200  }
201
202  /**
203   * Flush any batched changes to the database.
204   * <p>
205   * When using JDBC batch flushing occurs automatically at commit() time or when the batch size
206   * is reached. This provides the ability to manually flush the batch.
207   * </p>
208   */
209  public void flush() {
210    db().flush();
211  }
212
213  /**
214   * Update this entity.
215   *
216   * @see Database#update(Object)
217   */
218  public void update() {
219    db().update(this);
220  }
221
222  /**
223   * Update this entity with an explicit transaction.
224   */
225  public void update(Transaction transaction) {
226    db().update(this, transaction);
227  }
228
229  /**
230   * Insert this entity.
231   *
232   * @see Database#insert(Object)
233   */
234  public void insert() {
235    db().insert(this);
236  }
237
238  /**
239   * Insert with an explicit transaction.
240   */
241  public void insert(Transaction transaction) {
242    db().insert(this, transaction);
243  }
244
245  /**
246   * Delete this bean.
247   * <p>
248   * This will return true if the bean was deleted successfully or JDBC batch is being used.
249   * </p>
250   * <p>
251   * If there is no current transaction one will be created and committed for
252   * you automatically.
253   * </p>
254   * <p>
255   * If the Bean does not have a version property (or loaded version property) and
256   * the bean does not exist then this returns false indicating that nothing was
257   * deleted. Note that, if JDBC batch mode is used then this always returns true.
258   * </p>
259   *
260   * @see Database#delete(Object)
261   */
262  public boolean delete() {
263    return db().delete(this);
264  }
265
266  /**
267   * Delete this entity with an explicit transaction.
268   */
269  public boolean delete(Transaction transaction) {
270    return db().delete(this, transaction);
271  }
272
273  /**
274   * Delete a bean permanently without soft delete.
275   * <p>
276   * This is used when the bean contains a <code>@SoftDelete</code> property and we
277   * want to perform a hard/permanent delete.
278   * </p>
279   *
280   * @see Database#deletePermanent(Object)
281   */
282  public boolean deletePermanent() {
283    return db().deletePermanent(this);
284  }
285
286  /**
287   * Delete a bean permanently without soft delete using an explicit transaction.
288   */
289  public boolean deletePermanent(Transaction transaction) {
290    return db().deletePermanent(this, transaction);
291  }
292
293  /**
294   * Refreshes this entity from the database.
295   *
296   * @see Database#refresh(Object)
297   */
298  public void refresh() {
299    db().refresh(this);
300  }
301
302}